{"_id":"@sberdevices/assistant-client","_rev":"536-2c2ae8d5a22bf546ae2dddfdafbe21f5","name":"@sberdevices/assistant-client","dist-tags":{"latest":"5.0.2","canary":"5.1.0--canary.6.6cb0d2c05b1b21c8391789a76cd71dd9d69c44e3.0"},"versions":{"1.0.0-rc.1":{"name":"@sberdevices/assistant-client","version":"1.0.0-rc.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"UNLICENSED","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","react":"16.13.1","react-dom":"16.13.1","styled-components":"5.1.1","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/styled-components":"5.1.1","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","file-loader":"6.1.0","mock-socket":"9.0.3","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"gitHead":"77190c3dadadafa9f102eb877bcdc25033a78053","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.0-rc.1","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-H7pgQY6Xj67AQTEovH/v1ne0qtOKxeTwfrilAavFTw4gC94n/ei7Ec/mm1eGHZxVHwOGf7qoGRlHodDQQhbIFg==","shasum":"ec40bb82ecb7df4ca78d297d692c03a5fcd0d06f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.0-rc.1.tgz","fileCount":54,"unpackedSize":560164,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJffwA9CRA9TVsSAnZWagAAdKEP+QBywBrWWbWHvlh0pzwq\n+JHeHSbmeHeVXAbhWLAhXwsj735cjukgNsRT9PbuIGSo9K6oAEho073Ey0ye\n3HTPYzdQmqev3Vg/zq+Xj2/zQre0Kh4ry200P5dumQWexJxGyszoAyyuPmYk\nM7eOUGQWBdkNEsPiDD4xbStbaw4vvmZzpVK4JHeerLndr63z41is+nL+0w7L\nVL8gNj9k6ETnQIwkWT76QtyAp2EVzfVL+iuWLuXLEIGwW3PNOR/XJnDMXFWI\nVQtqXnpnsEeeSu8eep8kGOqnq6Bge5lp+h0wURPWjS1xt2tTRPU+5CUrW7t1\nEzeE9XJZqhRg8mpWMpxbM2OGdlbSveFXGfNYKh4PO3DIWEIMvWUy+Rn/MYxt\nwUvJ+54LjemP3AG5GUpZJhPPwRGrQlB5vzE9rH9igEBaZpCU1Trsc3xr5xPO\nZTjETrTwimze6eUKgJAMY6I+VIyk8oSeMuVu0kEknaAhtvHFoVzDBR79FdpQ\nrxjUmELOMsNXgG3OLwqTVRbVOa3cAgUA/8o8lXWXcliv64gMG7S9bhxX+63A\nFjx9ITmQxn1O+57b5hal8Rau6MFbIEmd7qzcMQ9lOkZo0eCdxP5sr7UfFsNx\nN1F6qNFOsIkxZujk/7wFfdEFIO4JjzJpN1T2IYAVIp6NPsvcwvSbmuqvTpAr\ncR8n\r\n=qtBY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEPhgVIZyhjISYP+yf0ZXirTlURWD1X2Lf2a1h/2O/GlAiEA3DHliTrCggji3rNdubUrSJvJCSvNuzgpnbjcZ13itog="}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.0-rc.1_1602158653354_0.5627825868684309"},"_hasShrinkwrap":false},"1.0.0-rc.2":{"name":"@sberdevices/assistant-client","version":"1.0.0-rc.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"UNLICENSED","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","react":"16.13.1","react-dom":"16.13.1","styled-components":"5.1.1","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/styled-components":"5.1.1","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","file-loader":"6.1.0","mock-socket":"9.0.3","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"gitHead":"9cbf47a74c98ddc53a8b7c566297860badf9ecf7","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.0-rc.2","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-JiOs1CJ4TsHvTTSyOmp13GgYsXYstmg5wBSDOOLqP60q20F0UZvOTI7F0U70SfxYjKR8dKGcebM+yQENKRKtBg==","shasum":"fb44cdd67659e55eb70cbfdc2c7057fb59e36ebd","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.0-rc.2.tgz","fileCount":54,"unpackedSize":560217,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfgD7vCRA9TVsSAnZWagAAnYoP/RkG4N9CKYALOZHlJHM3\nJH5ZXqlBHd95vK/XEDG/YjZDrwcAsDUhzfJnQeXMwZFQxR4oBqdthCse9Onv\n7SGGpJjjH4tdMjBsOD4qZTt5JhcbNI+80NikGHfqepGBBpZdsumHTcvS3Ev1\nmkCTHzRAEu9RAs/sPNBPY8VWtfju1Kq+BvsWqLaeUaWBVv/kMKLGtM9uJOEE\nM7JZ+keOSxuOJ/tz7kCySVwqJ3UYgd9Trnhlr7Mi/nMzsQPF3umaH8JwCi3B\na7WM09w/uRZDbpiogjnbyd1mQwpKK5jCAqh1/Ge3QMK0xQUfEV4MOQHbsxGD\nb6XaHrMfZYEUGPszMQJJ4H6esq1Rkx91iKZJboCc1A1BoKr5AQusHBNQZfYM\nGSahdtAZYL1/S1MVvLYYY6PIm3WT9A+2Os9ihElJzQcaJcMCdf4az5fyjAvu\nSDER18gMbY4zWDs0l4D1Ydq1we9nNpFEiiJmQc77jaZJHn7IbjpyJsKlgY1l\nBOs0T5tqB5L8nWyoybMptAery74pInAzgjWtjwq0cg6jVYisCxtI92SJfJt7\n9dEPzF3+SMEKhkYQzTDvhpIsFesPu0KgY4MKlboxOf/B2R7xFeXB3m2vGC/F\nXNO8ElowfoopggCqovjlkHf3EB8e1TnGmPKBF15OW2eJxrTiIzdm7PhED1nL\nAAW7\r\n=6x/j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCqJRAUoa90XTMxH+a5+TNmTJ/iQL0ncNaJ+VzmSXZVVAIhAOruihD2n2rEHTtVcebUqi0ggeAZjrfr/XyP+6E6FjyG"}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.0-rc.2_1602240238744_0.0030399752105150224"},"_hasShrinkwrap":false},"1.0.0-rc.3":{"name":"@sberdevices/assistant-client","version":"1.0.0-rc.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"UNLICENSED","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","react":"16.13.1","react-dom":"16.13.1","styled-components":"5.1.1","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/styled-components":"5.1.1","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","file-loader":"6.1.0","mock-socket":"9.0.3","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"gitHead":"bca6d4018d9ee5b0e5eb6a84b2395f8b9e99c158","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.0-rc.3","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-4vhl3voRuRBCBFdgH3aj0EDYVfcdJjTSNWiU+SGv255pDYG3QVB6Nskh85aNb3mty8WrUJ82CepGEtR+H+kO7Q==","shasum":"23de3c094680abce067aebe4b1170de408436dcc","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.0-rc.3.tgz","fileCount":54,"unpackedSize":559281,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfhYs3CRA9TVsSAnZWagAAmBcP/0CZDr/gcxuB0A5jOs6x\nLSOpDO1dWnrS46mw36yUhGRHKNW/qC5dbYLsRlIHu8cVdP4RQi/QSmrEFBvI\nKxbSxl6hOcyXyFjLulKSZLNZCLPj7+fhBZb6b5emSpHJ0tvltTokPzqwvtqB\nt2l/dRhV7k9seBYXNJ7bmbcoK+spSdaa58iGnbOIGshv9s1sYsMrxFq5pCGz\n2pI+RGXkQTpyyzL6pTes+45BU5+hTDsTImZ7HrCDLN73RFOQXqEEka6PY1qd\n7YRI6BZKMNIePgakyvgqV46ZoaYPd/JMpnaKXd/i3WtkLIsS5DM8Nu/MvfVL\nOI6jNb5gbR5JO1755zZCSA6xMu/G9GPaLCUjTODk3HLBEM1QhATp6dSTMDQ0\na7Rkj/tpE2lY0vJ3cgMq0poo5NJyPK0IAt7jjE3gs/EHDeMNN7SGP4aypko4\nlsgtGqg6ypyhsvB+FjE30N9fJ8mfQpGfq+MrHmTtZqi6wHXeBp9PxeGSjgRg\nXaWi3fYiPMtdBj3qg5LAfWJP31x0xmjhwNk7k1YeW7DeS2VapfA62AwRAnZx\ng58gkK4G1WHar17AoAyzytQMU+SpPFKm2pPMmbz6QXhKpvygQ3DmD1pi1tYQ\nIuFIl3KjUF7pCjgDTfDUPSOYgHz553YK20s0WPyCzM2oWjOFjKbYMEYNRvIX\njoLj\r\n=xMOQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDz6zOzGjQ4UXLz3wTS7kaiTkbR599pqFuzz080p22b0gIgWpdvANlLTQ3APJ/c3Vc5xsXiPZGgXvov4NK24shOYR4="}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.0-rc.3_1602587446993_0.3690955055915428"},"_hasShrinkwrap":false},"1.0.0-rc.4":{"name":"@sberdevices/assistant-client","version":"1.0.0-rc.4","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","react":"16.13.1","react-dom":"16.13.1","styled-components":"5.1.1","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/styled-components":"5.1.1","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"gitHead":"6f19a4d2cf4b6b49f055f79c4bface4a9d8d43ca","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.0-rc.4","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-AxfiC+vAA2CKFnrcRnvAJRBtYD9rXzWcHVo+t2Wc0bo0pwEstt+8SfJd6zszYTArADthNSnB0VJ7yy4ZUz2uyQ==","shasum":"99eb4483f835ec99a473996fa3b41727fec668c7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.0-rc.4.tgz","fileCount":55,"unpackedSize":624181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfkqVUCRA9TVsSAnZWagAAdwkP/A3NllEbAwdrgWKfh7AV\npw75Ms4yqsY53yZCPrgF/CbpsLNgCQ8aWUvSc4GD9rf2dmxeW1oJrszvnxFn\nT6AKadeCJCIc2RKnCpc68ukuJUPVPxmKibghm25Dznko6FlSpbeQASaUBVrA\noq4jFCeWGv8RGN1gN4ii1d1vTU50OTskVC6luF4bR6C2GvjbkYpLI53oV/ks\nIdOhL8C5+KlzrjUgyk9m1k3FN+cFuzD2WlAM8nzGrBctha1LHsGox/xiZT/b\nZ06ACPC3FFnn42/8X8xHqZORceg0O79vNC8ZIqQRtAsCSy60atwqD3b95wSz\nEMaMuR0acYkDjQcKc2OKiyNj1kODYv00m7MWMuiqcxNRSydciEOYOQ0hc4ap\n/IVnHmCT0rm3n88kS2QQYBsKdeGbw25ANy8eIIdeO7njd52nElbXnCgDzqKp\n5CQW8Od/2e+2jwLLhRS/XUX3wMhPtU7vT8U18FsLIJ6cYaLPFb7IpJ/8fjzm\n39DYYlGp7Jai3SoqDdNO8huVid9PUuusOLzRXjIeSFWpdf6yHuSYkUqprxX7\nzdHih9KYZLagloNvv6Ji7lMVa16M0zPfsKEHVwq9DcC197SHO5c/NxB31eIn\nRghibWdty/pAEEwN5RsoJ1BInRcei0Nnadci0y1zz+ldDxvsCa8IHR8mQl+Q\n5QkW\r\n=0dr7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH7BWbSXfsbSGUYkpMYxRow7bDF0Ec8dtHqphCu9T39sAiAcAD3VxlsK4f+cLmbi3ebj2S5D83S3IiIGyhAQjGUCuw=="}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.0-rc.4_1603446099511_0.11303347386645535"},"_hasShrinkwrap":false},"1.0.0":{"name":"@sberdevices/assistant-client","version":"1.0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","react":"16.13.1","react-dom":"16.13.1","styled-components":"5.1.1","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/styled-components":"5.1.1","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"gitHead":"c4a078602e9313f1c69233d4b5f9e7075dd65087","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.0","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-F4PAytcxyPw5JCqDykh7Mkq95cOgpHLtgok8gnfTTk6pOtcbFSA7tbkovUrJfgoOrojVtl4GNz350cCn/BQv6Q==","shasum":"09b1c657f8132cec8d158abd6138066a516b1ec7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.0.tgz","fileCount":55,"unpackedSize":624176,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfksVBCRA9TVsSAnZWagAA4NEQAJbqk1W5+NrJ8pvK6KBZ\nMbW1Ku5j/koMCwK3811U/BWOljAJFh9bLjkvQv7D1WyfcPeZ84LPozGQc4ea\nUikBP1R/on59PCO5n2BAadWwpdYNkIw/VtS6ZCbpkkppxCLS5j9ATRaAatfk\n/cZAk2fBoDbjxyiXUWpolUItUeoIUMtp5jK9LTn+rQW6fNEaoLZgOYsEXzRc\nK8np1yL2Y/6TNxIAwPiLz/BXd0gLMHW+LgBGddHkxMQZmzQJlx1ck0Bau66O\nd++pl94cV5hcE4G8kEdr7E0n7pRj7MW+b+fcyPK5s1nvu4aMQ+6cCYwkWLH7\nhwCzcNN/j/Z+p3F8KZnwVrCREkKUdPEyVMWXruINWHUylqv+MxmWQxLvAhyG\n3ycqruHlbFb5VoSccw46HOKeZrxtoHOer+ysNQiy6KrzG3/4UdyhuNC3Kg1x\nSx+FRAgFTTDy4j4lp2Z0FMImgtbG5wASUOP9t2EZMFAK1g388Tt4X9I0ybRJ\nfdHLiY99Gq6j8f8FiDXmeZHCIcYOUlLeif6XoWL9nWqDRVsLcpfyxkC+G5QQ\nTKDqlNkVUrPT0h5KDEPvIxQzHHgqippZdrOJqhJ+gkGj9t6SQo9eiNnN1Gdi\n9OFtAagMF8MR6Q/163BGwkJsp8l5GMEpnvycoao7VNcjE1nsJ5EIciP7jTsz\njKSI\r\n=1xXi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC0+A1To9183A89b/lkQSWVh3gzaPripVazk+SgBizeWAIhALUvjHipL85t6cxqsHykSebv4T+USZtsULa/qy2KSeZg"}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.0_1603454272960_0.03289328115191026"},"_hasShrinkwrap":false},"1.0.1":{"name":"@sberdevices/assistant-client","version":"1.0.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","react":"16.13.1","react-dom":"16.13.1","styled-components":"5.1.1","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/styled-components":"5.1.1","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"gitHead":"8145db42f42200a9f82cc836d3906e251c2a5175","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.1","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-pDtebaRCxCeiQeOvrbPTfrbhJJpJK68iT0z9+Tgv77o+JLUPOjXCUw82bDgiCOm0aA8TXglCLcY9s2gGCavC8Q==","shasum":"1c37d8a6f00a8301b51e5f349f483c430c95f12c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.1.tgz","fileCount":55,"unpackedSize":624469,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJflsaZCRA9TVsSAnZWagAA0yYP/3IAAU1gK5mNjhQbTE0x\nJyG/wOsnR5Vh6wGCL7+wvRMg4RmwLIFMT/vOMe5zZUIRx04tWAwzZFnLwcyN\nHBadqEs00mxWJA7Qygzg3xzOpvOTw6zEqC3lxL4yvOn7q6V7VuwqzHBnnla+\nvzp1mu7I+F/RJvBm9c4khnelexlemRYZl5tadM872genbQqdMenQudyLZliL\nxV36GImqxkgr0BT69tD4rPisZM8L728wjqyeEKhsO+c8OCTlyjW+qoMj4/PS\nyUR0F1wXgDO+OkEbR40DORgBqmhhSfjFEVP3iZooEr+lfF1mZa8mMXGGH2qV\nfN9IzwfVrMhAud4b1vjDvkLi35F1C6K7hWpVoVUdBO75nhdhbgcNCOEcQ6uo\nV2THYBzvAXsuQqjE11gsbz5EhpRvq8/T6SVaJ0/S9U5p6oark3NW43ul5rBS\nmmAyQXKut8QVYNXrFKgE82W4IHyeKU5eiod0pIz0V0CuzJl8YABaQe9G5xXd\nxsYmvOVA/U7RFoduN6U7z8kqyXLQo+EZF4b790oxLCyte9OaDpjZzauGATdk\nLiQDNZBfurzlpF+3xg30aIIdiLXqP6plhsRl90luQD/mBJEM7/LdToEw2C0W\niggvr6a877CzTCpM2AfiuyrLZe39MCp1tjbeUFmMPxLgwXW353cu5GMJqMDx\nc/Lm\r\n=zKAn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCv4J1duUHWOusk90rUJ24/4CUzQj6C4h+5/0OvvfZkcAIhAII/Mxr4f27tozAOdESptCX0KrJcQlPTDNCt6nJOKTkO"}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.1_1603716761101_0.9424545613315036"},"_hasShrinkwrap":false},"1.0.2":{"name":"@sberdevices/assistant-client","version":"1.0.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/styled-components":"5.1.1","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0","styled-components":">=5.1.0"},"gitHead":"51a46d11e76019ff3e781ca44b38a783193aea17","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.2","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-vOFBQzatqt0fSEmxzW5Xo2uOlXuGnPTHmh3SyiE2al4Eu0zwVcYNXaFLweYlTWB/mVleTBXIuxfBXQoj18SD1A==","shasum":"63351ba6fd92315ef4633e781d2db8599cfcd622","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.2.tgz","fileCount":55,"unpackedSize":625430,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfmAn4CRA9TVsSAnZWagAAGQAP/1NH2tKP5a3e62pgm5zZ\nD5QyVCwFg7sDGULErOIlfOD5Pv1MbsYVi18F0IIBF7l5yqZ5Jlh7yBqIhsge\nXxoUckMeA+d31Ayny6Fq9n7thzAMt/APV1ruzCvckgEnwUFWUygWAyzwEj9z\nj7UamfPw1xzLz3g/wEmHDqaJ4WPF3wkSzwsU2+716VKuo7+6/f/A6aDVNqTk\njhDfjUE7ZCW5+J3CzI/DnElP0OaLS5pqN7yC39qstwj19Pq+aa68MXs/+sSj\nvyqZxUq1z/Say1yHOi6cF9ay/APrBEvacdW/7mXCaH7BFAtrJBTRPWgl93Ss\np69j5Gx7gjqWS8Z4gvGmSsqJzj3bjGywo8kOev5LPQXkweAzeYkYEGx6fMl1\nGNRcQp0dP7w+4sKvLC2Zgm1spw4OrB/pGrboYZlYRW8I0hexio5A5Nww/Dkf\nJQxhUhBZb6z5dKxJYw/JeXqYltJofDvBA3kP0o7zMp33Z4eE3VGVxzb3F9V/\ntpslCL3OePRPBH0RUcghmJx0AVOWNnMQiRuMujASa3ENmfhvlnUfllItJ8+c\nxH7jv9YSM3ObB92lp5FQQlvSrbp8N1Xr2ZGI8P02aTlbHWtmVMecoihNX6Ey\nKJXPoIIMdN8d4c+WuyabadAczeu4Tlz4NVBFiQYQp5uvKGGEjjLP6z9MYhia\n9Exd\r\n=e7DX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDTYQb9CKpyKYCXZlnTi9xIbnt8FinIuPAbvvyi2oKttgIhAOsX+Nd57ZYeQ+OzU9TI2NyUjay90Bm9H4deFeW7tDJi"}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.2_1603799544231_0.04334533764761295"},"_hasShrinkwrap":false},"1.0.3":{"name":"@sberdevices/assistant-client","version":"1.0.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"gitHead":"526f0f257036546f91d92297252f2708934fb443","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.3","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-T0kWC9BPBcqVPg26oNORlbUbVebF1E5MjdzCgRE7R7aMf/R3dfLUD5bJhz6deaDiDTH+cCsJGuVRYAACV3sdtg==","shasum":"c1a5cbc554a54c088a5f52a3375f5d388f99bb03","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.3.tgz","fileCount":57,"unpackedSize":625270,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfn83mCRA9TVsSAnZWagAAfeQP/0+6RywxrZmqkO2GBuM3\nGG7BDZjcGM0ml84gMOpVlJcd4wPliH0xUpnYqwItaZ7EbPU40DYR+/+aTViQ\nacPw1BmbbE80DX98Czz5fqffdNMM7KIYD6TDpdQ1wYa7ZX7LvOwIcVAEuTBj\nMm6B3E4c7HoP1r9i8JDGIc3g15WEeX8Zd2dhMZmoJdgk5jRRU/XBT3H6yV6c\nZwbs9Wjl0+MrOfjPI6siHadGLPD35Ai9GmrBSyMQa+oMgY9W0jZJjry1BQ4q\nmuelvtAmKXZ0M4Ovt6/UDuXnn8cIoRadFn1miOlWrj/xqFYUZEwsDHBdsqx9\nwjFYqB7gjQScs2Yg3aRcfjE9q5kT6/9eDQuC57miw6RbYkN1oC3iiJaMlxyp\no4XxkPlGVQZAYZla2CB3TTLA7uU5uM9twiUnmrJd4buu7piz3TLDH+D8OiQH\nyPOGfShXBzrZN6fIYm9RzwEIoNrCz8GtZdEAHjmaXpinpccgEYHMjxSG+3jH\nnqAv1ICk61uVWrRaRZQk6ywUp4NDMEnEDNz+3WxY/X5SYo3PhiXBsPIs0VMk\nS7lqNFDOAYttAz44KuOX9BpNLYdI0t1LYLq/o6i43IYj7NpSCBrVrPASXsUt\ny/EsAA1ZR9i1wPgWpMJ5alv2jGxJ0yJKSUXkpn44UrzsQOE9YeHxftbVW5Nj\nDTMD\r\n=USJD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIARvoFR9LJWyc7Z+NytkqshXtnEUP+lyGX4QwyKf3ez4AiEA8RqqJIOcSvMynQxYEqwWexvZra5fBqM+C5DnHBrb+qw="}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.3_1604308454017_0.9228813035231254"},"_hasShrinkwrap":false},"1.1.0-canary.28.0039888976b7c4d0699434907891c4e88b639e7e.0":{"name":"@sberdevices/assistant-client","version":"1.1.0-canary.28.0039888976b7c4d0699434907891c4e88b639e7e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"0039888976b7c4d0699434907891c4e88b639e7e","readme":"<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\n# SberDevices Assistant Client\n\nAssistant Client - это инструмент для тестирования и отладки СanvasApps c Виртуальным Ассистентом (ВА).\n\nAssistant Client интегрирует в WebView JS-код, который предоставляет биндинги к нативным методам на устройствах. В режиме локальной отладки и разработки Assistant Client эмулирует нативные методы, что позволяет запускать ВА в браузере.\n\nУстановка:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n## Quickstart\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            token: 'токен разработчика из Smartapp Studio', // Токен,\n            initPhrase: 'Хочу попкорн', // фраза для запуска аппа\n            getState,\n            getRecoveryState,\n        });\n    }\n\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // подписка на команды ассистента, в т.ч. команда инициализации смартапа\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // отправка ServerAction\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient), обязательный параметр `getState` - функция, которая возвращает актуальное состояние смартапа при каждом обращении к бэкенду. Используется в production среде на девайсах.\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient), добавляет на экран браузера панель с голосовым ассистентом, подобно устройствам. Панель позволяет вводить команды с клавиатуры и голосом. Также активируется озвучка ассистента. Используется в development среде для локальной отладки и разработки.\n\n| Параметр         | Dev only | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState\\*       |    []    | Функция, которая возвращает актуальное состояние смартапа.                 |\n| token\\*          |   [x]    | Токен.                                                                     |\n| initPhrase\\*     |   [x]    | Фраза, которая запускает ваше приложение.                                  |\n| getRecoveryState |    []    | Функция, которая возвращает состояние смартаппа перед последним закрытием. |\n\n#### Панель ассистента\n\n<a name=\"AssistantPanel\"></a>\n\nПо-умолчанию, в режиме разработки, панель отрисовывается. Вы можете посылать ВА сообщения, используя текстовое поле ввода в нижней панели. Чтобы отправить голосовое сообщение, нажмите на иконку салюта.\n\n### AssistantClient\n\n<a name=\"AssistantClient\"></a>\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n### getRecoveryState(): any\n\nВозвращает данные, сохраненные при последнем закрытии аппа на устройстве. Данные сохраняются при помощи вызова getRecoveryState, в момент закрытия аппа.\n\n#### on('start', cb: () => void): void\n\nПодписка на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nПодписка на событие получения данных от бэкенда.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет сервер-экшен, который будет передан бэкенду.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет колбек, возвращаюший актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет колбек, возвращающий объект, который будет доступен при следующем запуске приложения. Данные можно получить при вызове getRecoveryState.\n\n#### Формат объекта `AssistantAppState`\n\n<a name=\"AssistantAppState\"></a>\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. То, что происходит на экране у пользователя и как пользователь может взамодействовать с смартапа в конкретный момент времени - ответственность смартапа. Assistant Client, в данном случае, некий буфер, который хранит состояние и предоставляет его платформе и сценарию смартапа.\n\nКаждый раз, когда пользователь начинает говорить, Assistant Client вызывает коллбек getState, чтобы получить и передать бэкенду состояние экрана пользователя.\n\n```typescript\ninterface AssistantAppState {\n  /* Любые данные, которые могут потребоваться Backend'у для принятия решений */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    /* Список соответствий голосовых команд действиям в веб-приложении */\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  /* Порядковый номер элемента, назначается смартапом, уникален в рамках items */\n  number?: number;\n  /* Уникальный id элемента */\n  id?: string;\n  /* Ключевая фраза, которая должна приводить к данному действию */\n  title?: string;\n  /* Фразы-синонимы, которые должны быть расценены как данное действие */\n  aliases?: string[];\n  /* Сервер экшен, проксирует action обратно на бекэнд. */\n  server_action?: AssistantServerAction;\n  /* Экшен, выполяет действие от имени пользователя */\n  action?: AssistantAction | { type: string };\n  /* Дополнительные данные для бэкенда */\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенду нужно понимать что скрывается за единицей (какой элемент у пользователя пронумерован единицей). Ниже пример стейта, который позволяет понять бэкенду, что пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Кола' },\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Пиво', number: 3 }\n    ]\n  }\n}\n```\n\n#### Формат объекта `AssistantServerAction`\n\n<a name=\"AssistantServerAction\"></a>\n\n`AssistantServerAction` - это любое сообщение, отправляемое от клиентской части приложения в бэкенд. Оно может быть как привязано к ui-элементу и приходить с бэка (в основном, для message-based аппов), так и формироваться самостоятельно фронтовой частью аппа при обработке событий внутри веб-вью аппа..\n\n```typescript\ninterface AssistantServerAction {\n  /* Тип сервер-экшена */\n  action_id: string;\n  /* любые параметры */\n  parameters?: Record<string, any>;\n}\n```\n\n#### Формат объекта `AssistantCharacterCommand`\n\n<a name=\"AssistantCharacterCommand\"></a>\n\n`AssistantCharacterCommand` - информирует смартап о текущем ассистенте.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n#### Формат объекта `AssistantNavigationCommand`\n\n<a name=\"AssistantNavigationCommand\"></a>\n\n`AssistantNavigationCommand` - команда навигации пользователя по смартапу. Большая часть навигационных команд может быть выполнена стандартным средствами Assistant Client. В платформе виртуального ассистента есть стандартные фразы, которые обрабатываются единым образом. Они обрабатываются и приходят одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  /* Тип команды */\n  type: \"navigation\";\n  /* Навигационная команда (направление навигации) */\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n#### Формат объекта `AssistantSmartAppCommand`\n\n<a name=\"AssistantSmartAppCommand\"></a>\n\n`AssistantSmartAppCommand` - это команда для передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  /* Тип команды */\n  type: \"smart_app_data\";\n  /* Любые данные, которые нужны смартапу */\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n## Разрешения устройств\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого, необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Рекомендуется настроить эти разрешения на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n## FAQ\n### Как получить токен?\nЗаведите аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/) и создайте приложение типа CanvasApp. Токен доступен во вкладке «Настройка профиля» в секции «Auth Token».\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0-canary.28.0039888976b7c4d0699434907891c4e88b639e7e.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-hZkepusOjBh/BnZMpTsZPwhU3pqW7ygI2/p61Yh8YcCVqt6XpzwqKtV+uorg7KbnMAjgkx41crcX6OPcjRo44Q==","shasum":"478731113010f896cba57bf7d3896ea09ba90c60","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0-canary.28.0039888976b7c4d0699434907891c4e88b639e7e.0.tgz","fileCount":57,"unpackedSize":625820,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfoPQfCRA9TVsSAnZWagAAPDEP/1lK11v9kGvgsAgT1OvK\nzYdrTZFFAk0VaBNvAbYwd4LSIGaMlsuSe6XrXho8t5IL2Ar/dbY1rDrqEBg2\nDhhaFsz/Vf0L/5+9fMD/qYveUkQdncxSWjuU9gzsg5yJ/QZofU8ZYvlCekeZ\n1Xfl6VCWbL0JEe06XKrdocaEV39kPFDOMVNV7xhAuMHG4ttbNtbQbk5Jz2av\nCgwpm2Ox4DuL0jwrToBwp0XomHJ0tlLN8cCC51rcwBMZgrbx3mvnpgtvvteF\n4KV8/S1GHeq2ZFuB0iJD1+sTR0USIBCuTC6Isb5k9UZNgFP+gMeFzpG3xejP\nth4nq9PKFyi74FdY8Ua1rdhS+Bp1fAKpqStHWULFy7raWAUNTbmRQfivjcxN\nC9lZvCNUqiBzKY8iaylYFPVA5gdeMqetdR14HxdYG+PF9HJPTfN32SubCFwD\nebEshYteDVMfLPdEHKbBMcq3RgsyDSSVgk6UoFYb0eyKsTMXMuN02wtR89DC\nYrZQza5FeznNNOjL9a4hpHGUB0xCmUCUJhoXnsWlgS90nC9q7raqYUOvNdgC\nEWmsn6AM4k+g41RNSkQ9C/5YkMStpRy50/RyZnHdfHxrvHRMLaDH8BDaEC21\nfNd/C0V5flLeEhPDQaP+eey0uDjvTINXjHRRaRZ9RRD4pPmaqxqrRahkBSoS\nwyEQ\r\n=Y6nI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBHBdQD6JYC58D0pbqTFmIikjOdlBMqF2JbjKXaYLYArAiAyt0VQunzmd3kpV5zpWxVjZG/4WSDeknvKcCiircY3TQ=="}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0-canary.28.0039888976b7c4d0699434907891c4e88b639e7e.0_1604383774563_0.27709841998685225"},"_hasShrinkwrap":false},"1.1.0-canary.28.e8024722f14a1cd664ff356e2410446479f3d54b.0":{"name":"@sberdevices/assistant-client","version":"1.1.0-canary.28.e8024722f14a1cd664ff356e2410446479f3d54b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"e8024722f14a1cd664ff356e2410446479f3d54b","readme":"<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\n# SberDevices Assistant Client\n\nAssistant Client - это инструмент для тестирования и отладки СanvasApps c Виртуальным Ассистентом (ВА).\n\nAssistant Client интегрирует в WebView JS-код, который предоставляет биндинги к нативным методам на устройствах. В режиме локальной отладки и разработки Assistant Client эмулирует нативные методы, что позволяет запускать ВА в браузере.\n\nУстановка:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n## Quickstart\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            token: 'токен разработчика из Smartapp Studio', // Токен,\n            initPhrase: 'Хочу попкорн', // фраза для запуска аппа\n            getState,\n            getRecoveryState,\n        });\n    }\n\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // подписка на команды ассистента, в т.ч. команда инициализации смартапа\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // отправка ServerAction\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient), обязательный параметр `getState` - функция, которая возвращает актуальное состояние смартапа при каждом обращении к бэкенду. Используется в production среде на девайсах.\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient), добавляет на экран браузера панель с голосовым ассистентом, подобно устройствам. Панель позволяет вводить команды с клавиатуры и голосом. Также активируется озвучка ассистента. Используется в development среде для локальной отладки и разработки.\n\n| Параметр         | Dev only | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState\\*       |    []    | Функция, которая возвращает актуальное состояние смартапа.                 |\n| token\\*          |   [x]    | Токен.                                                                     |\n| initPhrase\\*     |   [x]    | Фраза, которая запускает ваше приложение.                                  |\n| getRecoveryState |    []    | Функция, которая возвращает состояние смартаппа перед последним закрытием. |\n\n#### Панель ассистента\n\n<a name=\"AssistantPanel\"></a>\n\nПо-умолчанию, в режиме разработки, панель отрисовывается. Вы можете посылать ВА сообщения, используя текстовое поле ввода в нижней панели. Чтобы отправить голосовое сообщение, нажмите на иконку салюта.\n\n### AssistantClient\n\n<a name=\"AssistantClient\"></a>\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n### getRecoveryState(): any\n\nВозвращает данные, сохраненные при последнем закрытии аппа на устройстве. Данные сохраняются при помощи вызова getRecoveryState, в момент закрытия аппа.\n\n#### on('start', cb: () => void): void\n\nПодписка на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nПодписка на событие получения данных от бэкенда.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет сервер-экшен, который будет передан бэкенду.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет колбек, возвращаюший актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет колбек, возвращающий объект, который будет доступен при следующем запуске приложения. Данные можно получить при вызове getRecoveryState.\n\n#### Формат объекта `AssistantAppState`\n\n<a name=\"AssistantAppState\"></a>\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. То, что происходит на экране у пользователя и как пользователь может взамодействовать с смартапа в конкретный момент времени - ответственность смартапа. Assistant Client, в данном случае, некий буфер, который хранит состояние и предоставляет его платформе и сценарию смартапа.\n\nКаждый раз, когда пользователь начинает говорить, Assistant Client вызывает коллбек getState, чтобы получить и передать бэкенду состояние экрана пользователя.\n\n```typescript\ninterface AssistantAppState {\n  /* Любые данные, которые могут потребоваться Backend'у для принятия решений */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    /* Список соответствий голосовых команд действиям в веб-приложении */\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  /* Порядковый номер элемента, назначается смартапом, уникален в рамках items */\n  number?: number;\n  /* Уникальный id элемента */\n  id?: string;\n  /* Ключевая фраза, которая должна приводить к данному действию */\n  title?: string;\n  /* Фразы-синонимы, которые должны быть расценены как данное действие */\n  aliases?: string[];\n  /* Сервер экшен, проксирует action обратно на бекэнд. */\n  server_action?: AssistantServerAction;\n  /* Экшен, выполяет действие от имени пользователя */\n  action?: AssistantAction | { type: string };\n  /* Дополнительные данные для бэкенда */\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенду нужно понимать что скрывается за единицей (какой элемент у пользователя пронумерован единицей). Ниже пример стейта, который позволяет понять бэкенду, что пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Кола' },\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Пиво', number: 3 }\n    ]\n  }\n}\n```\n\n#### Формат объекта `AssistantServerAction`\n\n<a name=\"AssistantServerAction\"></a>\n\n`AssistantServerAction` - это любое сообщение, отправляемое от клиентской части приложения в бэкенд. Оно может быть как привязано к ui-элементу и приходить с бэка (в основном, для message-based аппов), так и формироваться самостоятельно фронтовой частью аппа при обработке событий внутри веб-вью аппа..\n\n```typescript\ninterface AssistantServerAction {\n  /* Тип сервер-экшена */\n  action_id: string;\n  /* любые параметры */\n  parameters?: Record<string, any>;\n}\n```\n\n#### Формат объекта `AssistantCharacterCommand`\n\n<a name=\"AssistantCharacterCommand\"></a>\n\n`AssistantCharacterCommand` - информирует смартап о текущем ассистенте.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n#### Формат объекта `AssistantNavigationCommand`\n\n<a name=\"AssistantNavigationCommand\"></a>\n\n`AssistantNavigationCommand` - команда навигации пользователя по смартапу. Большая часть навигационных команд может быть выполнена стандартным средствами Assistant Client. В платформе виртуального ассистента есть стандартные фразы, которые обрабатываются единым образом. Они обрабатываются и приходят одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  /* Тип команды */\n  type: \"navigation\";\n  /* Навигационная команда (направление навигации) */\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n#### Формат объекта `AssistantSmartAppCommand`\n\n<a name=\"AssistantSmartAppCommand\"></a>\n\n`AssistantSmartAppCommand` - это команда для передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  /* Тип команды */\n  type: \"smart_app_data\";\n  /* Любые данные, которые нужны смартапу */\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n## Разрешения устройств\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого, необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Рекомендуется настроить эти разрешения на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n## FAQ\n### Как получить токен?\nЗаведите аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/) и создайте приложение типа CanvasApp. Токен доступен во вкладке «Настройка профиля» в секции «Auth Token».\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0-canary.28.e8024722f14a1cd664ff356e2410446479f3d54b.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-3+BqHzLeYNEqg1C/GdnDn5pLCTagYDFl757dzfUUXYGSZRRMn0CMyMKAHd4UC9I7On8jIvlivr5bZbSZBPCPQA==","shasum":"5b27b70b16c83bfec491ad3f52aade457f94333e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0-canary.28.e8024722f14a1cd664ff356e2410446479f3d54b.0.tgz","fileCount":57,"unpackedSize":625820,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfoPeSCRA9TVsSAnZWagAAB6MP/0xDZl7IouF0+QoHI6SG\nt6Go08e5HuALrj8W4KsVJFatJPy9uBqYjElBEJqwx2nYKHFUfwHpzBWOLkWZ\nRDm9yEm0fGrIPDZvocgsDonZw4DorPaL3pizjIZrd/LIVdrNcyVafrlJYEfX\ngrDF1ggFuU7pPPhcV/lzWAJvhP50SSq0fAlepTtIM5wl0ABZl99r+8nPFB1Q\nICk4/xGVU8A4NH2J9RGK5/8JSObePxPcbVfWlZQ8l/ETCtW4Q5V0GZQ8Ysc+\ndeK7hO68MF81Oe6uIYU+u+86Vv0clAfCqGsPDIqT5s081v6sxkJV89vPIaxn\nECwgZ0bTRCS+R1ZU1pAlhPUpAr3qDWxDfrpufsCZ04F3C6iNhdFGLIF8hTte\nDNddMMuwtS8TJ7TOFkdjZ5SXEgF15rNvBy2X1CYcZFD5n+OmwQFUyw3Id+MN\nMKkcXkFgOWqb7h7VvzGQnLlqhPyNBuIFcfzgUCvoGJQYLcVP7j5Ibb6Tl0QU\nKsJzAHvAX7VgyYlvQadF0QCBiMZHLdIMNjuK1wqWyl9B+LHp2+EMEffcYiqq\nbc4z7NIHqH9LCbuOX3NsZ7Pn43+VF7o22xh8Qx2wf5xMvIR2LyrIRSKdRYE1\nFU7PfXORKYmKVJ/HHXsIsc7iu/CIng80gBbGwNO+vd4r2sh7qVbkjvvGbphX\nowPg\r\n=qo3u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF9T9DWoEwSKOUDPROc+WDvnhehA4CHBp/3NCOXhkFX8AiEAgOUp8NmlcHBve4Yedvey6rKgx44LL8cTCm639umyaBA="}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0-canary.28.e8024722f14a1cd664ff356e2410446479f3d54b.0_1604384658324_0.5486407543055407"},"_hasShrinkwrap":false},"1.1.0-canary.28.ff63d06e164fa2ddc145ac23fdbbddeb98343c9f.0":{"name":"@sberdevices/assistant-client","version":"1.1.0-canary.28.ff63d06e164fa2ddc145ac23fdbbddeb98343c9f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"ff63d06e164fa2ddc145ac23fdbbddeb98343c9f","readme":"<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\n# SberDevices Assistant Client\n\nAssistant Client - это инструмент для тестирования и отладки СanvasApps c Виртуальным Ассистентом (ВА).\n\nAssistant Client интегрирует в WebView JS-код, который предоставляет биндинги к нативным методам на устройствах. В режиме локальной отладки и разработки Assistant Client эмулирует нативные методы, что позволяет запускать ВА в браузере.\n\nУстановка:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n## Quickstart\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            token: 'токен разработчика из Smartapp Studio', // Токен,\n            initPhrase: 'Хочу попкорн', // фраза для запуска аппа\n            getState,\n            getRecoveryState,\n        });\n    }\n\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // подписка на команды ассистента, в т.ч. команда инициализации смартапа\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // отправка ServerAction\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient), обязательный параметр `getState` - функция, которая возвращает актуальное состояние смартапа при каждом обращении к бэкенду. Используется в production среде на девайсах.\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient), добавляет на экран браузера панель с голосовым ассистентом, подобно устройствам. Панель позволяет вводить команды с клавиатуры и голосом. Также активируется озвучка ассистента. Используется в development среде для локальной отладки и разработки.\n\n| Параметр         | Dev only | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState\\*       |    []    | Функция, которая возвращает актуальное состояние смартапа.                 |\n| token\\*          |   [x]    | Токен.                                                                     |\n| initPhrase\\*     |   [x]    | Фраза, которая запускает ваше приложение.                                  |\n| getRecoveryState |    []    | Функция, которая возвращает состояние смартаппа перед последним закрытием. |\n\n#### Панель ассистента\n\n<a name=\"AssistantPanel\"></a>\n\nПо-умолчанию, в режиме разработки, панель отрисовывается. Вы можете посылать ВА сообщения, используя текстовое поле ввода в нижней панели. Чтобы отправить голосовое сообщение, нажмите на иконку салюта.\n\n### AssistantClient\n\n<a name=\"AssistantClient\"></a>\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n### getRecoveryState(): any\n\nВозвращает данные, сохраненные при последнем закрытии аппа на устройстве. Данные сохраняются при помощи вызова getRecoveryState, в момент закрытия аппа.\n\n#### on('start', cb: () => void): void\n\nПодписка на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nПодписка на событие получения данных от бэкенда.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет сервер-экшен, который будет передан бэкенду.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет колбек, возвращаюший актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет колбек, возвращающий объект, который будет доступен при следующем запуске приложения. Данные можно получить при вызове getRecoveryState.\n\n#### Формат объекта `AssistantAppState`\n\n<a name=\"AssistantAppState\"></a>\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. То, что происходит на экране у пользователя и как пользователь может взамодействовать с смартапа в конкретный момент времени - ответственность смартапа. Assistant Client, в данном случае, некий буфер, который хранит состояние и предоставляет его платформе и сценарию смартапа.\n\nКаждый раз, когда пользователь начинает говорить, Assistant Client вызывает коллбек getState, чтобы получить и передать бэкенду состояние экрана пользователя.\n\n```typescript\ninterface AssistantAppState {\n  /* Любые данные, которые могут потребоваться Backend'у для принятия решений */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    /* Список соответствий голосовых команд действиям в веб-приложении */\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  /* Порядковый номер элемента, назначается смартапом, уникален в рамках items */\n  number?: number;\n  /* Уникальный id элемента */\n  id?: string;\n  /* Ключевая фраза, которая должна приводить к данному действию */\n  title?: string;\n  /* Фразы-синонимы, которые должны быть расценены как данное действие */\n  aliases?: string[];\n  /* Сервер экшен, проксирует action обратно на бекэнд. */\n  server_action?: AssistantServerAction;\n  /* Экшен, выполяет действие от имени пользователя */\n  action?: AssistantAction | { type: string };\n  /* Дополнительные данные для бэкенда */\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенду нужно понимать что скрывается за единицей (какой элемент у пользователя пронумерован единицей). Ниже пример стейта, который позволяет понять бэкенду, что пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Кола' },\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Пиво', number: 3 }\n    ]\n  }\n}\n```\n\n#### Формат объекта `AssistantServerAction`\n\n<a name=\"AssistantServerAction\"></a>\n\n`AssistantServerAction` - это любое сообщение, отправляемое от клиентской части приложения в бэкенд. Оно может быть как привязано к ui-элементу и приходить с бэка (в основном, для message-based аппов), так и формироваться самостоятельно фронтовой частью аппа при обработке событий внутри веб-вью аппа..\n\n```typescript\ninterface AssistantServerAction {\n  /* Тип сервер-экшена */\n  action_id: string;\n  /* любые параметры */\n  parameters?: Record<string, any>;\n}\n```\n\n#### Формат объекта `AssistantCharacterCommand`\n\n<a name=\"AssistantCharacterCommand\"></a>\n\n`AssistantCharacterCommand` - информирует смартап о текущем ассистенте.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n#### Формат объекта `AssistantNavigationCommand`\n\n<a name=\"AssistantNavigationCommand\"></a>\n\n`AssistantNavigationCommand` - команда навигации пользователя по смартапу. Большая часть навигационных команд может быть выполнена стандартным средствами Assistant Client. В платформе виртуального ассистента есть стандартные фразы, которые обрабатываются единым образом. Они обрабатываются и приходят одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  /* Тип команды */\n  type: \"navigation\";\n  /* Навигационная команда (направление навигации) */\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n#### Формат объекта `AssistantSmartAppCommand`\n\n<a name=\"AssistantSmartAppCommand\"></a>\n\n`AssistantSmartAppCommand` - это команда для передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  /* Тип команды */\n  type: \"smart_app_data\";\n  /* Любые данные, которые нужны смартапу */\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n## Разрешения устройств\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого, необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Рекомендуется настроить эти разрешения на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n## FAQ\n### Как получить токен?\nЗаведите аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/) и создайте приложение типа CanvasApp. Токен доступен во вкладке «Настройка профиля» в секции «Auth Token».\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0-canary.28.ff63d06e164fa2ddc145ac23fdbbddeb98343c9f.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-0S51v5clS5oFmHkRMbfJlB/rT1/SJ1KXh7P33CvgTpnTEA2CnuuHYNqP8V4nqnJEVIOCu2NdJAPQC51ADrJumQ==","shasum":"7302405de98ea9cb426cc64732dff0d476cd2c5e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0-canary.28.ff63d06e164fa2ddc145ac23fdbbddeb98343c9f.0.tgz","fileCount":57,"unpackedSize":625853,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfoPikCRA9TVsSAnZWagAAO7AQAJQcmHLbxmvgoZy+Y8Bn\nZyk8NUoNh8Q1TTuO86Nf1E+N5wsP9FhCDe9MLVFBtBBlW2RuLKZLFKt7p/6Q\nvm3b7+G/j9kDqFLYMBLecqtqwuaRg0L9Ac03bbqaluuktL2/O6nG4p0bfCnn\nb4xu5nXBe32X3X7ePqwVvbM8a84oRuWY1FHlTZg/6d/UXVyRKF2YMuzdIRSk\nk8elMPPyYsfaghSfsLTzGuP2lY0uwGlFGJc8mdZpIzGM2nk31kGU3xf6CgOf\nSPV0rBPoa+k3WI6xoOfikOAjk3+Ua85k3S2Ko0/RTOJoCjdCNziWMFi9tYsb\nIsdv7NLROQrbU29G0KrwdgnCpg9nwaJkQWw9zGygDnPQG7bFMNClXenF6kef\nw19AZUoofR0LDyHX/5raAwkmNg780l0OIPNMG68tJnPJ/CFfn7XkskG3IYSQ\nU8o637Wq6IZNL32+D1xvpqp+FaaNqEaIvg95hte2/NGLnt+HhryF0gEXZVAt\nq7BXw63WDFt160D3QLdO/MAFj6Q3T01xWYOGbhIPdpVySXySdIqam6po/88F\n86N7gwdqgi6xD+jaFIU8isHIeLjShtTE3GOXliLpYJl6Si245krfB9ilNkh4\nFe03Nr7AcYAazLfPR8aRZB/GgruKpPh5P2Mg+YRrLL6omEXUX9qm/ClRa4Nq\nbV7S\r\n=eg7u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFcqXafXMkDZyu1HxKBn8CsCtH5js6WaodGK2yZ6wTU1AiEA18uoScSO+n4VfBvIONkRYJwDwOfWcBdCfFXeGmNznsg="}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0-canary.28.ff63d06e164fa2ddc145ac23fdbbddeb98343c9f.0_1604384931904_0.8921512588495066"},"_hasShrinkwrap":false},"1.1.0-canary.28.2b33222b202dc51c90c49b88daeddb053aa47458.0":{"name":"@sberdevices/assistant-client","version":"1.1.0-canary.28.2b33222b202dc51c90c49b88daeddb053aa47458.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"2b33222b202dc51c90c49b88daeddb053aa47458","readme":"<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\n# SberDevices Assistant Client\n\nAssistant Client - это инструмент для тестирования и отладки СanvasApps c Виртуальным Ассистентом (ВА).\n\nAssistant Client интегрирует в WebView JS-код, который предоставляет биндинги к нативным методам на устройствах. В режиме локальной отладки и разработки Assistant Client эмулирует нативные методы, что позволяет запускать ВА в браузере.\n\nУстановка:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n## Quickstart\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            token: 'токен разработчика из Smartapp Studio', // Токен,\n            initPhrase: 'Хочу попкорн', // фраза для запуска аппа\n            getState,\n            getRecoveryState,\n        });\n    }\n\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // подписка на команды ассистента, в т.ч. команда инициализации смартапа\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // отправка ServerAction\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient), обязательный параметр `getState` - функция, которая возвращает актуальное состояние смартапа при каждом обращении к бэкенду. Используется в production среде на девайсах.\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient), добавляет на экран браузера панель с голосовым ассистентом, подобно устройствам. Панель позволяет вводить команды с клавиатуры и голосом. Также активируется озвучка ассистента. Используется в development среде для локальной отладки и разработки.\n\n| Параметр         | Dev only | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState\\*       |    []    | Функция, которая возвращает актуальное состояние смартапа.                 |\n| token\\*          |   [x]    | Токен.                                                                     |\n| initPhrase\\*     |   [x]    | Фраза, которая запускает ваше приложение.                                  |\n| getRecoveryState |    []    | Функция, которая возвращает состояние смартаппа перед последним закрытием. |\n\n#### Панель ассистента\n\n<a name=\"AssistantPanel\"></a>\n\nПо-умолчанию, в режиме разработки, панель отрисовывается. Вы можете посылать ВА сообщения, используя текстовое поле ввода в нижней панели. Чтобы отправить голосовое сообщение, нажмите на иконку салюта.\n\n### AssistantClient\n\n<a name=\"AssistantClient\"></a>\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n### getRecoveryState(): any\n\nВозвращает данные, сохраненные при последнем закрытии аппа на устройстве. Данные сохраняются при помощи вызова getRecoveryState, в момент закрытия аппа.\n\n#### on('start', cb: () => void): void\n\nПодписка на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nПодписка на событие получения данных от бэкенда.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет сервер-экшен, который будет передан бэкенду.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет колбек, возвращаюший актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет колбек, возвращающий объект, который будет доступен при следующем запуске приложения. Данные можно получить при вызове getRecoveryState.\n\n#### Формат объекта `AssistantAppState`\n\n<a name=\"AssistantAppState\"></a>\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. То, что происходит на экране у пользователя и как пользователь может взамодействовать с смартапа в конкретный момент времени - ответственность смартапа. Assistant Client, в данном случае, некий буфер, который хранит состояние и предоставляет его платформе и сценарию смартапа.\n\nКаждый раз, когда пользователь начинает говорить, Assistant Client вызывает коллбек getState, чтобы получить и передать бэкенду состояние экрана пользователя.\n\n```typescript\ninterface AssistantAppState {\n  /* Любые данные, которые могут потребоваться Backend'у для принятия решений */\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    /* Список соответствий голосовых команд действиям в веб-приложении */\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  /* Порядковый номер элемента, назначается смартапом, уникален в рамках items */\n  number?: number;\n  /* Уникальный id элемента */\n  id?: string;\n  /* Ключевая фраза, которая должна приводить к данному действию */\n  title?: string;\n  /* Фразы-синонимы, которые должны быть расценены как данное действие */\n  aliases?: string[];\n  /* Сервер экшен, проксирует action обратно на бекэнд. */\n  server_action?: AssistantServerAction;\n  /* Экшен, выполяет действие от имени пользователя */\n  action?: AssistantAction | { type: string };\n  /* Дополнительные данные для бэкенда */\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенду нужно понимать что скрывается за единицей (какой элемент у пользователя пронумерован единицей). Ниже пример стейта, который позволяет понять бэкенду, что пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Кола' },\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Пиво', number: 3 }\n    ]\n  }\n}\n```\n\n#### Формат объекта `AssistantServerAction`\n\n<a name=\"AssistantServerAction\"></a>\n\n`AssistantServerAction` - это любое сообщение, отправляемое от клиентской части приложения в бэкенд. Оно может быть как привязано к ui-элементу и приходить с бэка (в основном, для message-based аппов), так и формироваться самостоятельно фронтовой частью аппа при обработке событий внутри веб-вью аппа..\n\n```typescript\ninterface AssistantServerAction {\n  /* Тип сервер-экшена */\n  action_id: string;\n  /* любые параметры */\n  parameters?: Record<string, any>;\n}\n```\n\n#### Формат объекта `AssistantCharacterCommand`\n\n<a name=\"AssistantCharacterCommand\"></a>\n\n`AssistantCharacterCommand` - информирует смартап о текущем ассистенте.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n#### Формат объекта `AssistantNavigationCommand`\n\n<a name=\"AssistantNavigationCommand\"></a>\n\n`AssistantNavigationCommand` - команда навигации пользователя по смартапу. Большая часть навигационных команд может быть выполнена стандартным средствами Assistant Client. В платформе виртуального ассистента есть стандартные фразы, которые обрабатываются единым образом. Они обрабатываются и приходят одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  /* Тип команды */\n  type: \"navigation\";\n  /* Навигационная команда (направление навигации) */\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n#### Формат объекта `AssistantSmartAppCommand`\n\n<a name=\"AssistantSmartAppCommand\"></a>\n\n`AssistantSmartAppCommand` - это команда для передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  /* Тип команды */\n  type: \"smart_app_data\";\n  /* Любые данные, которые нужны смартапу */\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n## Разрешения устройств\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого, необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Рекомендуется настроить эти разрешения на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n## FAQ\n### Как получить токен?\nЗаведите аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/) и создайте приложение типа CanvasApp. Токен доступен во вкладке «Настройка профиля» в секции «Auth Token».\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0-canary.28.2b33222b202dc51c90c49b88daeddb053aa47458.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-xTB6U1fiON9NTxfdPPD3m2ABGddeDucDAYSfDTkoLTdWKKMDcZWhkLWm8/+WDAUpxIOv5GF3MU4XT4cvoq74IQ==","shasum":"f2f792f3125d48ecc4210df21c6896adc28bb0db","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0-canary.28.2b33222b202dc51c90c49b88daeddb053aa47458.0.tgz","fileCount":57,"unpackedSize":625870,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfoUTECRA9TVsSAnZWagAAKSwP/RElhEGAMWZ9b+9LGHWS\nGY/nuxx9XsGnBE00GdPhIpuApWMIsJ/zYaHZx+OFYTfpKCfazzbZDqHEfnO+\njn4cj0Jp8NAv+SjMQ90JX25ygWhifwX/YGhmyCd9n1sBHoGXc1RMrfLpV0Si\n5e/hn2NIXOx39ciSVNEHnCTk4pI55eToXFMG00I/S5xGHGkrfRIRtyD+hvMg\n2J1RCr8/bO2lVTHZji3n7ALnrVztXb8sTwnzWbdvpb5lMRmTH5nfjnyVA7dN\nSV//Alw32jA9lsovCrN+OVUMGnasENd8+X5XH6O/+Ifimq8b29jz9hjibcrP\n/9BOPQcroe9CqjVjp4xFAUlIYmKJ+0UVPeO3s++G8h+o8V5v68b8FeGeTcJW\nP4mEFol8ZJxV9rvqCyit0DEZ052FiH3ADbjZm9ynLWiE+e6qGEwGKMl+kC+q\nZIzh5Bav2ENxH5+Kt86vYkdKNIcn6gPaEKsijCH84ZaQrNz4xlprixllNp7M\nZaBVk1c4MJ1E4/bMtxCWgO3i2pe2V7F1RnVobW+hoZyxJ8y5p/9Xbpfwq9aU\neEgF33QsZuGVSGP6/bBJTvgLAnLJB6lsWhzAMQD+5g1Z59o6gd3pINi+V8jL\nrlcuOZVdDCBXYwx9m3Q9RNKt+HZWp7qcZq64F+7/S80p26c7ttn5JGPWU41e\nH3KS\r\n=zFLP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDSrIzweoGxGP1MW7DVGnOohtNhH2Zfaou2TZmaSKOoMgIgBmDvWNNw6IA/fEkCzhEskhj26j7LocKJYHk/TUERtKk="}]},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0-canary.28.2b33222b202dc51c90c49b88daeddb053aa47458.0_1604404420270_0.7569074510855207"},"_hasShrinkwrap":false},"1.1.0-canary.28.c89484a6f50a3216ceb49b7e8f9b0c337de8b899.0":{"name":"@sberdevices/assistant-client","version":"1.1.0-canary.28.c89484a6f50a3216ceb49b7e8f9b0c337de8b899.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"c89484a6f50a3216ceb49b7e8f9b0c337de8b899","readme":"<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер. \n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#Пример)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [getInitialData](#getInitialData) \n     * [getRecoveryState](#getRecoveryState)\n   * [Форматы объектов](#Форматы)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)     \n   * [Требования](#Требования)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp). \n3. Получить токен в Кабинете разработчика и передать его в запросе.  \n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token', \n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн', \n            // Функция, которая возвращает текущее состояние приложения\n            getState, \n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState, \n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState }); \n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа. \n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\" \n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд. \n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода; \n* голосовые сообщения через кнопку \"Салют\". \n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n\n### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов \n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя. \nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом \n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений. \n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды \n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0-canary.28.c89484a6f50a3216ceb49b7e8f9b0c337de8b899.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-Uvc+nN7jd5Z//C/ptySfT28M1kuNbjAn1hb8Gz3+60EhTvm2/wCvAKkWtMthqQ1a2e1D5tZQlRRPs4j8udB8fQ==","shasum":"7bebc94ad5e449fe9652168c5f27df7a6e6cfaf1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0-canary.28.c89484a6f50a3216ceb49b7e8f9b0c337de8b899.0.tgz","fileCount":57,"unpackedSize":628699,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfo7YZCRA9TVsSAnZWagAAWGsP/0f/gQAGs/d78pcwV2PO\n/1ID+3mlWLjZQRxbSnERePCvqYVv3f2hupPvtEeJbs0sFsXHJoiZ4dGdaTmn\nvs9kHdfrx2VkCdvCE4FsIVtAkJU2fwpBkDV86MTVJUtv+ZFQJZ72GMcM5kW4\nlL7mvh5vNU++yhaPP0OtyOg5QNbW/MSuBLNAjPvaKwVWjEExSTkP8b4Qbibl\niu61vHhxDSss5Md50kiEPGYnfqsoB5QylFJKmzfRK74l85LJnaAO6KYEbLE4\nFeXFHw78qmqNIGPe9xzXUlY4nqR77Z3aySkq4egRrKEhjXpEAgL5iTSm1S+D\nv/34wdwLBLdsD3UVxUOS+6VQaSAtRXN2IKHmxLYXRndca1qhkfa7wA/LRBy5\nnKXQwb4WlWxn6/hMSIik9nZQLc7HzAavaOgA/DMSJsgMdqrTr7IypcKysLrR\n7+ZbneM9pun7VaVGJJTnNOFh/0LnxRtGGVN9/rThP7+Wx8n2iqbLXp1+OIWh\n1xwaRmWUgILnrgRYc3eZdqXPmY7SqDBbs52Gu6xxEcNtVRsElNFR2SwvDG7/\nZqfpbS8y2ctX0w81F3HEJQC6ZbLfSPrBuJlHYxPMD1PWeiosg4QNvhPFyhWK\nxXxAvlIR0t1DP7XQAWweWxDEDuy4QsXWUbJNRoymVVkoF/hleSflycPHkXBZ\nza/m\r\n=5g2v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAekG29/ydcW79848EzvL0R2jRw1EA2Ys1vCKCEcEXD0AiEA6nTGuurjjYTIFqB2aAPmiuB8fmq8NbUSSyKVCDeqIsw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0-canary.28.c89484a6f50a3216ceb49b7e8f9b0c337de8b899.0_1604564505121_0.3005741654458993"},"_hasShrinkwrap":false},"1.1.0-canary.28.7e205355c78b9375dbc6f799cf47f536201dfb5e.0":{"name":"@sberdevices/assistant-client","version":"1.1.0-canary.28.7e205355c78b9375dbc6f799cf47f536201dfb5e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"7e205355c78b9375dbc6f799cf47f536201dfb5e","readme":"<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер. \n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#Пример)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [getInitialData](#getInitialData) \n     * [getRecoveryState](#getRecoveryState)\n   * [Форматы объектов](#Форматы)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)     \n   * [Требования](#Требования)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp). \n3. Получить токен в Кабинете разработчика и передать его в запросе.  \n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token', \n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн', \n            // Функция, которая возвращает текущее состояние приложения\n            getState, \n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState, \n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState }); \n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа. \n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\" \n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд. \n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода; \n* голосовые сообщения через кнопку \"Салют\". \n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n\n### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов \n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя. \nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом \n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений. \n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды \n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0-canary.28.7e205355c78b9375dbc6f799cf47f536201dfb5e.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-5oieLqb/c7b8b8kr0KjzhnCbbl9ay4/3wtk48j7uWraLmQTcfjKqZCy7sYYOSJJ96ik1uoeylpHptiiQZkmLFg==","shasum":"f01aaebb90e9c42402f3ee79efe1cf881fbac5c4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0-canary.28.7e205355c78b9375dbc6f799cf47f536201dfb5e.0.tgz","fileCount":57,"unpackedSize":628699,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfo7dICRA9TVsSAnZWagAASH8P/0QDDXE3z+V6XKYvMrVv\nwuM/bieGgxdJc/SCok3gXQjGFvptxa1cbFff83LXUXcsJtgh4B8zY3GskFN3\nDeEQORvYZXkKYaTSjB78vZWBeQDmT5cap/mTHH1SQPopvSlAPFwWBZcU5XY0\nbf4Bil2mlrBWBe9Ku7/C0uv5infBAMJUmdf/xcOnTg0fv+b6Vb5AXMA/1qd3\nekTbu7TMmCgppwrTnFC1yUDnkRav5bmjhKlc7MvuSPkWim7B25MnMF5HJnqG\nf1JfzuhYHgLHn83YY1qi8JVB7T1wYk6y1bp11ewOz7/5D8sL6kmACHnjebXt\ndjUwZFGTKp4QF3laEadU5cGl+rZAtnG0L8A8SDzlIE/AEhfqk+LHFftXGOax\ng2NP+wVtH5Iz1dgzPZvrbrOGWgsd0/xqlc38ZTFnaXYWxwCAnSbniDN+8Nja\nA5TPeqUNhIVtdEplH2rxwp8r3JJOIOOt6brTk8yHXt42+vEiF82CqZefiBtX\nGE7Znzcg2AAQxleninF9EuU5vigKQMYKbJ/agAlUONeGkoYz/PIh2mkEx/ge\nAs8cQZqg4ppeGxkN15cf2jSteJ2rr2Yb3Pc3CO8XRVkg2MEsKv9gVELqF/wj\nIFl4MuXE1Pj+/76mgaQvlbBxVlPgbpeVkparkWmDK3qzT5x8JjBbqW+D609M\nU8Pl\r\n=sQPj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD3HnzHPO2BwHSKiclWHsUqBNtg3/v+Vu54O6+F83yxLAIhALu3itJeEIZeMVW2QM6cKQMJM2Bh0/VVw+Qs0xfyZxKt"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0-canary.28.7e205355c78b9375dbc6f799cf47f536201dfb5e.0_1604564807588_0.5106497020525689"},"_hasShrinkwrap":false},"1.1.0-canary.28.13b9cb7b0ded987d2540334b42c530f888514d74.0":{"name":"@sberdevices/assistant-client","version":"1.1.0-canary.28.13b9cb7b0ded987d2540334b42c530f888514d74.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"13b9cb7b0ded987d2540334b42c530f888514d74","readme":"<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер. \n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#Пример)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [getInitialData](#getInitialData) \n     * [getRecoveryState](#getRecoveryState)\n   * [Форматы объектов](#Форматы)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)     \n   * [Требования](#Требования)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp). \n3. Получить токен в Кабинете разработчика и передать его в запросе.  \n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token', \n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн', \n            // Функция, которая возвращает текущее состояние приложения\n            getState, \n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState, \n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState }); \n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа. \n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\" \n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд. \n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода; \n* голосовые сообщения через кнопку \"Салют\". \n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n\n### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов \n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя. \nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом \n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений. \n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды \n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0-canary.28.13b9cb7b0ded987d2540334b42c530f888514d74.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-/kG1N4B3GAqw26cj7ITJR9iimZO5ZmFNa3kWX02bHIoAnySRQR3t4RtfnLwdpmOfTQQ86hCda4/WqT1eTCXh6w==","shasum":"7fc6ec20e388bf0c2d2854943e6022501d8edbb5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0-canary.28.13b9cb7b0ded987d2540334b42c530f888514d74.0.tgz","fileCount":57,"unpackedSize":628699,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfo7hICRA9TVsSAnZWagAAedoP/i5k4GNeH3Ka8Zh3y+8I\nPyo9atq5h6rOTDMcRbBiyqiqNEmBatpvLbohyZu+1tpCSPy5TXWYA6fMWnLj\nINCx1bnoLoDAK5etcIah3RliG2J6FnQdfn/v5h8PY+hsS4D5Spv0T5rxbip6\nJRS0ew1cpTXc5yp+cqx2WEFCSi6kB+1mvmnoBof4hDF2jAX/bLvzRcCpNKEd\ntl9fM3hBde8kZHXqwDbCg8sDhVgu2CJ7J7ogsqONRtRdbcBf2+QHSEQwBD0g\niKO8KySYQ32EA0MhUJbId2T3QDOG4/WLnAVUU5Z68OZ+Lw7fYdXRRPnD94ib\nOqh6HgnKr3vKVaMdRZ8dQ5OsvZuxCijxibNAQ1rOw37iXwLudx3EyfG6VZy0\nXU8cQdfV2QzvL/OC/Z2dYwKHLPGLa5af3Z8abG4fl1Mj1D0dJz0uQwwWWIkX\nfjrm+gi+snEysotMxgyTyQlSCpGjsfem+vzDYkIaFFMdqyB1P0NKMYeTlgjg\nWlfLuIPZlLTUBrvDHkgXuCr1RlYb+3/55xjmYPKlfwX9QMYnt73qqxAlZ8r3\nCAq/lSMl4C8VoQcHse5v3BmaxUWkyQVArXe2b8g6IePk5+kkaoT/aPdap2Sd\nqtvu7qgIC9TDInbBaLFcTgXfTNHM2RAf2oZ9H7axQ6rEid2RICOcyLcTyfN9\nb15o\r\n=I8p7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCZmyFcwOnFIT3QKZVyie3xzFI5Skxv92+d3KI/JVblZAIgWl5rVQy4Ubu+iENNsuWFHYGsCQ+wDM5FNvcUd8TTwU8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0-canary.28.13b9cb7b0ded987d2540334b42c530f888514d74.0_1604565063459_0.9487187744128656"},"_hasShrinkwrap":false},"1.0.4":{"name":"@sberdevices/assistant-client","version":"1.0.4","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"gitHead":"529d39e87c99ad0c84b77cd544485ce19d86780a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.4","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-nD5pnCVSeyIY/dMtpjiQ1nv0FeKlPJ5GcPhUqSuALwgnwijR1+U1HUlbsghe2AoBKl5HX7eSOzLPhTMKaKEVzA==","shasum":"b776e28b63597d500268d994b92cc1abc0796d52","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.4.tgz","fileCount":57,"unpackedSize":628446,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfo9RSCRA9TVsSAnZWagAAxp0P/38SsNATFpGMIzacc2In\nS2/L8sz90Bl7dyUMOBJf5i3T9MSMiD1aSJfIaflcUJF53NF/ojhMlJbgibQt\noNzfOA8z4ERx4tPzsha9tYdi324GQ3n1l5uv0t7wCeN8VKIA3jVmFWplgU0m\nxTED+/9+E2jkJsmY7a/EXMofjS+4dckreQJ3b7sEry489g0tGyqVCEd+6Ftq\nhZZ94KbsuOCvseFCFF+VYRrjvjYj66rSvofeC/ojNquSvjskVYXwMjRsAJvx\nsZSYXwo+F6PZpbeJPI7uSwh5IWrlb1KaRS9AQPxpfbQCubZwUtWgelfAHX7b\nwOre4NCsaSpn44QzycaLiGmc92cW4tsnDDMppTa+rxLRzFWGxDmpw9lnynqB\nYx4s1FY1UZtB0MsC3HAgQxTqX8USSw4R/wIkw4/5+FU0NU/KaooDSW0mRQZS\nBeD/VSJzOcVcoYZCIihIVGhHeKsMmC8wD00eIa/V26/6kwB5ft/zHOGfMnv4\n6qjoQxEvPr/WX/5PfDWxx0xPb8sWZIxSVGWqTUg27y/BxTMZdNuGjwXC6Cn4\n+YtxOciHJxFEFnNaqP3nm2fHKva4+uPHt1mLOjFxgfbEKDouMDRPQ3eUfJ/W\n046xvDEC5BbjRpLqfVHb8KDHmyCAqjARzm/K8aDv3yN+JH3ziGB60qSDf4BW\nvmgn\r\n=6Ua6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICbt79Eb9mdorLEtSo/ZePY1s9taiUDfIC25gQe01jZNAiAsYsJANgnRsN/EF+GHTWQZV87mKIO07BIzKo8ivCi0Yw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.4_1604572241929_0.4109506893098458"},"_hasShrinkwrap":false},"1.0.5":{"name":"@sberdevices/assistant-client","version":"1.0.5","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"gitHead":"a9d41590aa80600b2589022228c7f1de5aac70de","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.5","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-iKYCYqF338OpRKp3jOLZ7SGjJcvc+k2Wjyj/LjIQ4LIbEnMrwtigBb40NENWLTFEIcrlll3gWtcxlUhKYgTRbA==","shasum":"3630e3c5e1e8803a47c3327e35167ec949589ad5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.5.tgz","fileCount":57,"unpackedSize":629108,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfqPUkCRA9TVsSAnZWagAAZjcP/jSUacsE0AOX8cOQu/++\nXbeydFUaQez6Ok+gf1tnvyXAm4M3L3yzHDdOKunw+vIhHdyst2hPHDL1HLZS\nHBEJPZ9f8U+jwJ/zbVT0T2v7u5DBHeBX2xmNuuNfZQ4f2Gg4hMwNtQi1Bn4I\n+URVlFERFkm8wHyv5nJAFxbiTIn6wX58n4Z36a0eVHgCKAjObL5HCnORPLnJ\nk36ke0LdG8OaAj83LQMu++kZnYMjufIQTUCV5Nc+96uqdtBil2e78vgIZVvy\n4pN5K1dPGAsKcRMFCV8gN4giFA6iiosEjW3XJjYJUhkpdsTr4liXOxMTb/Ek\nQG2jXf3L1a8/opYGEuL70q3bgkcPrjPB11RfpWDo9czwiRlDmwa0zxmyjaiL\nzMWZkr24U1W+4ueZbtOnwNnXxWr6DP6KSiGfXsjn0CdDhxEKTtEXm9DH0KvB\n8M+p7JqelLmWRJ1szNRFoLCkar2h8G62AISpqslqR8RsErqOnDN7K/Qm2Dqn\nS13WNwxPFMFFL6KuqMowqBPMY5a2zc3FHlcuAPbO3o2SLRH07xR8xOQj1ejt\nGCnbySsM1yBkfatdWuJ/mZCWP1A7D5aBrfo8EjM/BOFeCt3+PeYientk2DXO\nG24pH0bXPyKBAt1hajx2sPn4RoOnwIrhl7bboFleJsVohAMGcT8qdkQtgQlT\nOj+O\r\n=ZWaM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqDK6EFpnAC9Shs4TjmGm3eR7fwqP7F5oVYUExF7Zl5AiAuE91CaD7U2dVN04YWJlmD3R/VqlrlK+s+tMVjf7Py4w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.5_1604908323610_0.19846885708495132"},"_hasShrinkwrap":false},"1.0.6":{"name":"@sberdevices/assistant-client","version":"1.0.6","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"gitHead":"25a7fe2dddd9225aa0b4e027ddbe3f2db8794613","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.0.6","_nodeVersion":"12.16.2","_npmVersion":"6.14.4","dist":{"integrity":"sha512-2Z3Ulq27VM4tZAywCXMZXCV7fwnHRWES6O01u/+Ra1DqowvSZR8Iqlu0Mzbvpz9me9M3zJghFZ3oJuhs0ZLaiA==","shasum":"8a52b845ff222370ca155a5054df8b2be84e7fde","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.0.6.tgz","fileCount":57,"unpackedSize":629123,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfqsBtCRA9TVsSAnZWagAAoCoP/R+tftyANVtl5I0m+qL1\n3kWGmukXv4VMlkz6frAPJ0EV7rRpm1aE8KsbcdY9viI3jzUBXVmGzK08Lt5P\nOgAfkAVK+GIC85nOtl45m7iAXurZCeI98Xo1/5LktlbPDpF79+Of60t84VBa\nSGvRoCV4D9wLXjIYzxd7z83iIbBQrwqULLC/snxGk9DNhKk836F+9nvCdhdc\n7clhFLr1ShoHrDsxocabXaq6XQQPNvqU0HfCqlAluev9K3nbe5dNO/vBd7Fb\n4+bqeAQ7JqOD6WyfaZrXKbjhJNLGAWd9wwt4lrNLSwX0o03zbnSbp2omiYbi\ndD0ipiSDiKAfRIJBwzQiJmPJMr4wAqdW2AlE5AshpNYsW1zlTHqdh1CSjtL7\n6iPjyTuEhtIRQiC2UD3DMqceB39/wboQbNp1IX/KO4CJjksz+D13VwcjJTpR\nslzJyYw6iNhb0Vk8eNl9p2u49ldvCJs9cSAU/L/gikKiyh+jIwZstLdQ5Mld\niWPFqY6Nj7IuUXs1+Cgi1H2j2T7Ra0J2eicsg5ZlNWEOmSwDDL697n7gY85h\ngFHd13PczLiCyq/BklxhI4VrXjXXd3TI77JcJudHHIWe7QrTTsqPZvff4B+8\nGqJclisjAl4gtpWQ80BQhapOyzMhHrxJ3s1RLZ+VwUwQYiOF1Wj7yZkjxKWV\nTltf\r\n=aiUi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDW8AT4MNKeuKhEueFvroG0c7K7tkvkSsriWZbg+bHtQQIhAIEsNPc8CFTrALg6V2bcsKY01vIGkEnz3o9iMQK0/3lA"}]},"_npmUser":{"name":"turanchoks","email":"ipuncho@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.0.6_1605025901282_0.4289743973412574"},"_hasShrinkwrap":false},"1.1.0-canary.28.8847c2c108c0d53c38ae84dbae2c2d3661879e36.0":{"name":"@sberdevices/assistant-client","version":"1.1.0-canary.28.8847c2c108c0d53c38ae84dbae2c2d3661879e36.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"8847c2c108c0d53c38ae84dbae2c2d3661879e36","readme":"<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер. \n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#Пример)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [getInitialData](#getInitialData) \n     * [getRecoveryState](#getRecoveryState)\n   * [Форматы объектов](#Форматы)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)     \n   * [Требования](#Требования)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp). \n3. Получить токен в Кабинете разработчика и передать его в запросе.  \n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token', \n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн', \n            // Функция, которая возвращает текущее состояние приложения\n            getState, \n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState, \n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState }); \n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа. \n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\" \n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд. \n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода; \n* голосовые сообщения через кнопку \"Салют\". \n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n\n### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов \n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя. \nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом \n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений. \n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды \n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0-canary.28.8847c2c108c0d53c38ae84dbae2c2d3661879e36.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-omeVZHXrWanEDPotv780gHhMVPtw6VehOu3DcjauqnASE5OP/ub8XxVkKwWQLS1Geskf099l6E1KvQV+9oVskg==","shasum":"e15e804c48e1cac7bb6631ae47fae8c35d500346","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0-canary.28.8847c2c108c0d53c38ae84dbae2c2d3661879e36.0.tgz","fileCount":57,"unpackedSize":629706,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfq8FeCRA9TVsSAnZWagAAg40P/1aVj+EZ8n0QZeIsnIQq\n+/VCcKQQ8fQDk5fRwrDMvOM4TqMIzYqUbyLyLK5VldZ+LU/m+6pFExC4vx2w\nVLwQhv0Pud3TIm60KYIY4p6DKthvi2XxRKQ9YwZAWfy6rKW0WDZd53xNdzbl\nXYmdF+/RU5gqObM6H1s8D3INQwUFjIGOL3qs6LIPO49k6KtS1hG4YdjZDWvC\nKSEpCv4wqYJFSTMFs/VzG/2LDKDR/J6BQc1pJ5jwKJtdD4WvlR+jWvRCbXYf\n2/OPzhAt0IKq+Z2Hz2EbFqXJyTqePoUl5pgmfSSkR6AIKObAZn6UKSoKRFaM\nkPR1itIBTWrJpTKKqDSTAKoLGn6mVMeNP0Z354e4uHHDGNtDZkP1jySWz75U\nRfe9US4DOgLKnLyx0jYj0ERVishH7+N+zk3icjLU4GDrTW4AwfiMa3RrA7s+\njTrYQCT1zhgp9YBFl3PUhmeJ+kZXgT1ZJozgPAd07qwFRZlByC9mCwzAr9zx\n3k1FAhPwGWi0GBAywAeItw0yKSsORlDj95omwNOpI6bXtB9uT3MjCpuglVDp\nvWHTSa6b4AYL1dEcQMv6BJmODN6BTiVzSTukjnCYJ0DmNaRCxgkN5bZDlt/w\nCr2wTR+vAm2iLg8PjzhxaRuExMzAUbGoEVyRyPZKtHMgODgexX9HQXcjw1S9\nbad5\r\n=duKx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGHKgMUBW4vh3YUy0VKxHhVpW/fm2D2yjJFThBJZh8ZHAiEA236FqWYcWM597X/t+o2k5FInmWaoF/9B90Kddt00xss="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0-canary.28.8847c2c108c0d53c38ae84dbae2c2d3661879e36.0_1605091678131_0.11029185219585513"},"_hasShrinkwrap":false},"1.1.0-canary.28.fd9cb485eed455a9e6b47cd80b581d88d0b3b144.0":{"name":"@sberdevices/assistant-client","version":"1.1.0-canary.28.fd9cb485eed455a9e6b47cd80b581d88d0b3b144.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"fd9cb485eed455a9e6b47cd80b581d88d0b3b144","readme":"<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер. \n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#Пример)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [getInitialData](#getInitialData) \n     * [getRecoveryState](#getRecoveryState)\n   * [Форматы объектов](#Форматы)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)     \n   * [Требования](#Требования)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp). \n3. Получить токен в Кабинете разработчика и передать его в запросе.  \n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token', \n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн', \n            // Функция, которая возвращает текущее состояние приложения\n            getState, \n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState, \n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState }); \n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа. \n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\" \n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд. \n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#AssistantClient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода; \n* голосовые сообщения через кнопку \"Салют\". \n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n\n### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов \n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя. \nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом \n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений. \n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды \n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0-canary.28.fd9cb485eed455a9e6b47cd80b581d88d0b3b144.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-S5gfLqK+TgRYjA/oXwDPVzIEQVDs2hvZAc14uw/bJTEL8LVruCHozJKwByfDIwM+Udx0S3t/8VjCr5dGpWPk4w==","shasum":"6c5be66365c566c4349bb4a3a699c3438117c7d0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0-canary.28.fd9cb485eed455a9e6b47cd80b581d88d0b3b144.0.tgz","fileCount":57,"unpackedSize":631202,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrN38CRA9TVsSAnZWagAANR8QAJNVnwJBBEbIgOGfNIZj\n/ISrv5EF/pYR/l88Zpu7Xk/rXGdlasl1PV/N9PxlWQsSQ5jL38WjwV/YLmJd\nhxNyk3TgiFgYpRDryZKdgqBB5CFV/MMLG5sTudJS1/2neFP8ZJ5wKNmXnnwN\nFlrPxQKXXziSH0cbu4VlmfLEUOdIz2IlNIsZZ/r6zJTcOoyU/VJzZO1ckABi\nnCxQg7mb+S3KqStSm2/1Pbc6Qu6tLly/6x2WGI4y6OkZ/5HygEsUfK6y3Vnf\nq85Xdi4bdWHK4g6amrYxaNqUSiNgj9Xtugtoo/W4CaGV2F2EmIj/ZKbmebX1\nhzbCFjOS9jLKQPk+lYOETZZmnzqvviRoHyxfVNPZDlKNGyeBmyydVfHjCtmz\nGMjKeMCulcHY6oAqkKcFtR4CI8b/YuJuT98BbISx+e+IxWWpLZs/gGcNf7RZ\nc5UbSqBtEWHMwfCmsksoxqcOrX81M1Q2bNQGV0bYVwWhwQgUBZpqUu18otr2\nveHmQi56zaY3sNJ8E4RUhLcGvCYwedYp/9XFt9smE0LT40/eLn0K4ivQdX4g\njEolW7GA/BrSkahtNe8lc06V6ckJudlYb9LTBDaMlQdufjo33GBaqeEz3A/G\nL7aHL5xyF1RTCq29zlargAxnArv9n+3qiFWEt1GpLkvR37u/2n9QsR3teEHo\n1Gok\r\n=lf7U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA0+N9tRt6WuQN2eDCn+7cy/9uOIMqKi+jzRaBbaGVSoAiEAhgdzbjtQMe7DTL0ZSmhw4DoyU5VQlFNK+ERM8Zwg6qw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0-canary.28.fd9cb485eed455a9e6b47cd80b581d88d0b3b144.0_1605164539556_0.13792855762229972"},"_hasShrinkwrap":false},"1.1.0":{"name":"@sberdevices/assistant-client","version":"1.1.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"gitHead":"590d7ac1e37dbc87621c2a62b29c0b2959ef0f78","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-KAIzenmQ9PDvCIcTO35nXgS4SkLC5uGgON6EfM/TKFUqlTF9qyOSAZe8FyjXgGl2BBVdOyUxFnGGV8kOYlglpA==","shasum":"20f32b8beafa925bd68553fa1d3bab802b37949a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.0.tgz","fileCount":57,"unpackedSize":631028,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrQbXCRA9TVsSAnZWagAAz+YP/jYyMoNvrurtV9wtkWMy\njkjPOSUgbbkd0QzxJmiAgHl16PFL4wSPa3EA1wJIc3gmwZjDfXtvfRl7dkwY\nHKbeO6eHmEux5h+wtNQXyBfgfjZo5nsPZr3cuC2V0dBVGO9wPnj/EFVXKYm7\nbu0DOJbNT1sVT07ulvzjYCO7JBzZg/tZg4IWkel7a/WEs9QEjeAfDAm9/867\nmtK3ScN/xIqNuA8kkgYhNQYyPTWPbc7lQx56WFQykPEplcS/NuwGT+1ScpnQ\nAteYXGXuXsJ/ihpa8HDWStvmmrn1Z5kk2+HDs2TalLy0RO+s35JFkHVE0orL\n+GEixgtUBQIYwZve+TfPHblnYiuvV0s9ejCUTXpy/e2csG6P+ECu6BBVtr3H\nh1vxy9q+74xZOF9f6PdmshRESc8lTt/17z6LswZ6T0ua+I8B/EATFD0QLJAQ\nQMQbS0nv/PuJdMPxuSd+ZGApsML8oEu+YlhqmtE8i9nE5y9vyn1kHUeVg2yU\nmXb5jCQwAX/WhATFB0hnT6wqN9Ag31SH5voqu1dVuYhq0ZGgP89o7s5A/TYI\n5QVpKdusbtolxZzCQEv3hkr8cqaJ/vA4BSuztH+NCDu41sVT+xhwWrien/vL\nFRiGbGrjgm91EC6zJRgO9k3dnPpWG+hQnZd6fQ79bR5AoZaTmpBWKu21VvMo\noJvc\r\n=gVaW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCT+JLaGVLLfS+D5NYVX4ykBJgOsbcewRONq7Dv3Zpl2AIhAMQpC8JW0ApTYaKsUuS3xoBeo1u2pkeX1pbrpl/ToBbQ"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.0_1605174998790_0.8016104148123258"},"_hasShrinkwrap":false},"1.1.1":{"name":"@sberdevices/assistant-client","version":"1.1.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"gitHead":"9f2b9dfc478a88e3ffba3d47ee0f6af33f2a1df9","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.1","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-CgRlJGmlzJCVI/XSIEi0kpbNhGbF64EGHdSoKf3FBlt3XJNKJHydvBfr05UFZocgIFEfNKlq3aht5PkekRc41g==","shasum":"eb1c18a7b0dd681f886f3b5947c68476b4642400","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.1.tgz","fileCount":57,"unpackedSize":631198,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrUfHCRA9TVsSAnZWagAAhScP/3W+HSz5ljRIynjYK77p\nVG2n45wKQCu/FDTyufooKMGwHrO2uWhlG46otqvfuTLp6gF89hQiMe7PE3OJ\nXvZBJaC8sbMYNVKZFNOHB8eEThHlIDIsIG1ObE93JIL+NKPHIq8r6UWPnXaf\nim0x/WO+8GSL4iCIyV+eM0QVXQ4OJVEeGQTVwSdywOtyomHduT/7LO0t4qQn\n5gNc1QdssPRc7ZMrMtOHbsqb2VUELJtD6uEHB9FM1ug1vcpqE8ZM6Rvv9xHe\n5yEwSDOCsx/leLaYoo492dT/0/hUU0zULMCW9zrQZDfp9zntnu/BTKgqYkDG\nxzuwnnP2dggB607mvL86vPeEYkxEk2IHYaUWh0AViOHSrV0tZnRCEFhbEV4A\nFpFaW6t3kqeB7GDv802IqBzsTIXWRz2Isk4rp0APDJRcLpTVDhLlT9boiSKD\no/5VTlvjTpTbo4lNEFHa0BWYVxi9xsLWD5Cx3mLYMOTTM8dG2ZAexlfOyubh\nUq95mngsNp0+IKQMWwJ2BFewiK670+ujB2T1xQOARvsyWQ1KLhOjEyZEAiQ4\nccTY3GbpaGqKdnzg7hwgPRRcVUnc6L/RRpvl0Oz03lvdnoAkHcaCq/ww4k7w\njExvADkK5YMATEWRjBDq3loKDOTVzRs6F4HJlVTLxkmNspOg8xcqoR5/TMOI\nkgry\r\n=oE1j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDAbEj9pN84U9bhu9KIbhR/U0alWsmV+iAQC5d17UKzMAiEAreSxcdTeZWomgqWsZ909RJHiS/CVVLurcXMgLYbaUg8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.1_1605191622547_0.7574628568667632"},"_hasShrinkwrap":false},"1.1.2":{"name":"@sberdevices/assistant-client","version":"1.1.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"gitHead":"80307fa711f65242ec2f7996d3b758c47bf3b81b","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.2","_nodeVersion":"12.16.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-/fylnmfpZOJVsfwCgAYERRs2L5xKqdYBSQ7pu2X2d72c/Ad5sMbPvDiTZ60/+1DwrhMcizW176drYCC8mc9RNQ==","shasum":"d3b90833816d7fd1c5c823109bfc5c919a20345b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.2.tgz","fileCount":57,"unpackedSize":631196,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrUwaCRA9TVsSAnZWagAAPXoP/i7UNC0B2GSyvDxWn5ca\nXJkBOs2QZbp+7XAJABxrAFyrxX0xplA3j0IkgRwlczT/kv8N8qtba7935LwC\nrV6t9r4G85XE1gixPUKu68nA6oNpsTba4p56RLMj73Jd1+AM7CNCMaHHcPdt\nZhbQlv102KsDRdy6C/kbBEUaqHTZBVaDAS1FlnUhsjx9L3CCTgGU/tflWUuN\nukGzpiO7FFL9xsn2fhncwV5mTjUgEXAxRBYLRcVwrF2Cmau4GcZ3QwrmjkUF\ncsSc7/2US6ha9KTiBUPoSyL9JOFF5x5yPMBglDGYFKxFErIdaFq49tNIBcLF\n0iP1+Ou2DRcA/m7PU6NBwBNmORewOvtsHRAjkl2nh6tD5+r562ouqnMzfYJ/\n9W5E568mVe+CymWSW7H26bMq/zJxb4Pi/bzxFBxcPpan3Edy6yIJCXPlcAAY\nnNmtHZJbO0oJv6tc48GgiYZbCVgfp0s4YVb8m/9g01H5kA+Xz0FA9ANv8Nj0\niu4KhtF8HobtfBIt6y6ml8OFkQtcJbAAjs+vSwJhFqkQUyoGDvcBesouSau8\nT9aMiZXqc1iDmys+Bzfze12OYakweVrEbgjG3wthOTjZWQUPukFudFkSO9xJ\nAYaoEHMh7BOqgzStR1pT33ZUjqu8nixH4cJJbXLawa6x9U9NZud7uOnSchPx\ntOmv\r\n=TLl8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGHeTIZ8MaLTWDzel/3i/vDENIiIOdoLlWeSBny+OKnyAiA8mQ9FagNx4JF25kDYMG70flH1M6HOgXLQh/Uf84O29g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.2_1605192730246_0.9450865990196797"},"_hasShrinkwrap":false},"1.2.0-canary.28.37f04d67478d37b5a6faf8381a0082f32783ae95.0":{"name":"@sberdevices/assistant-client","version":"1.2.0-canary.28.37f04d67478d37b5a6faf8381a0082f32783ae95.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits"]},"gitHead":"37f04d67478d37b5a6faf8381a0082f32783ae95","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.2.0-canary.28.37f04d67478d37b5a6faf8381a0082f32783ae95.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-jnLBQsFNUS95cZCmFz514MWdfTlt+aDNKUhSjErWtDphJT7OOVbHcCNvxXYrhwEw639WP/pvphxHyfwPG56Qcg==","shasum":"3bebf6f234995c7004390a649f9058b5a28f6ab7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.2.0-canary.28.37f04d67478d37b5a6faf8381a0082f32783ae95.0.tgz","fileCount":57,"unpackedSize":631964,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrmY3CRA9TVsSAnZWagAApBgQAJQs9rGFUVA15dN8vo4I\nhg0U17HGwVpbmEg4JA508tk1uiwiBdcak6xB2Ev0wX6qzLXq63oRgMPHTDzQ\nokc5yqrPGPR5WK37ZIy7AJdxTHbZDUpq8NCL54NuRP6HU9/yKlVeydPVEZlm\n3mQAUX2Bw2biBw6w4R2R8kuWwBZYOkQuqKUn+sKAc4xnNqG+KsthrtsOJCkK\nXBxV9g3RKYGIc5LJStAqsGMGHciBFWfhESNClAid+b6VAvft7n3PM7yThpbK\nfG1yElGTiG0GiGKoxliK+2Kb7yY4wAvSigAPyHC5se0y4LdU+CfBomSpcLdv\ndUz/HMstEcaPUWtpcIuZfTQd93radEi+WeS0afmTdI49kO6z8E8lX3VfXYor\noQ5RyHJujc8ppl/GSWBi1mgdaGEXMl6p+Y+Ii9Rv/B23E4CodQQ4wOBONP/r\n4qfF2JIODJa4+hS+JSQeGVON6WtO8EVagnjbWWxnLfjDvAY7OmmlZ+Nk5cEu\nx61k304omSvUlEBhCdUqJMkFSeLr6OWyrdr8+OzgEuBgvvubXBfSZlQ/YsEL\nid/9lHME6XH3JS5cZO6UeBzsEDa1SiSQ61vKvDdNerVotYChfTxNAIk6YNBf\n+bMfSxURKuc6zSNAoWth3vMfuoGV2VQDeZWRj86BhWl6ULNLCS5cnhzsVh8y\nrNlS\r\n=LIWp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFK5dOAW5KBnxaSY3oB704agsPwiRyGk6TmAe97DKEE/AiBiK1dy8+vdKV4B6J9FpFuR6Xl7wTcEmCuSGW3nrJ0T4g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.2.0-canary.28.37f04d67478d37b5a6faf8381a0082f32783ae95.0_1605264950827_0.5516009248121405"},"_hasShrinkwrap":false},"1.2.0-canary.46.35f944b7f71f70699a407c0fc41749c063fb49a0.0":{"name":"@sberdevices/assistant-client","version":"1.2.0-canary.46.35f944b7f71f70699a407c0fc41749c063fb49a0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"35f944b7f71f70699a407c0fc41749c063fb49a0","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.2.0-canary.46.35f944b7f71f70699a407c0fc41749c063fb49a0.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-Yt9CO/taEZUsMPXqS+y4Dk3Bi+eSfIrP2OBh55VbLayW+pVANVloPHJ9ypAPxQrq8oqNFtUGd9q4sgcBJKK68A==","shasum":"27c83d9238c1dfa5ab467e10b582d5c81572024f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.2.0-canary.46.35f944b7f71f70699a407c0fc41749c063fb49a0.0.tgz","fileCount":57,"unpackedSize":632022,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfrq4jCRA9TVsSAnZWagAAMX4P/3lNSPwmJDzSHyVOyQPV\n/5u9KrtQCIWjr9FLxhE2MWitiNbDUcclzCKtTvc2VIguw7ZpAowmtc8emBDz\nQC0aXiGuI4kO9ORqQkIBiyJQV1/r3s4+06+hs/j4FtZFH3w1K2sbJEPQBfjS\nSrsX4Y/4KZlb+X/tiXxshbQqhZ2EbV4gpODs7XfwPGYjDQq7tDGnfpGh/txf\n7dsZ41bChPgwUXOf2qPOYbteiX4z+XplQYKNkftmtRxj59rODhiM0if8CcMp\ndfL/NBbBZkZUR24uFwWPBtwEsWNXwzWBd4qmKm04imSukSmk25G1w6jLG5vN\nQ3JjNbWaXRA3sQb/Ii7dY4zSXWbF+Ox3J2+gIo4uRxhSLuRsXgUL55gO24Sg\naoPORXRJj1fVcCG680jTKhKw5nwI9UHF5wnqywXa+HFV/M0dQ9/9GXZgeH/p\nbYcpNY98EYr9AVV/M2zFkYqyRHoxRkCNTHVF8OywCl8GnjeacgqBX5TMoSBa\n8CvsAJ1RjAzp7AZmB06Y1Rh5Nssr51w9Bl7mGf60CM5EPBK3OOEO3rige359\noxuyPWGpRi7OLqooFBAQQg89piFbU2KCCSO/zQD/ltMPTJoYkvlEQLHBMb/X\nY1EsOk6/X1+ALwj5jfFaMozAL22G9MXhjG63FuDyX6GO4kAJqwtvzg5aH7QN\nZfOL\r\n=gVfL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGiJlu5kw7caM4IL8/d0aXzwdaUPjvaBMvgODcZfnUIJAiBWSgOM68lLMAUO28zfgkrBNo4nB5qjQpttjVANjxDOsA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.2.0-canary.46.35f944b7f71f70699a407c0fc41749c063fb49a0.0_1605283362935_0.6689761399300478"},"_hasShrinkwrap":false},"1.1.3-canary.47.c977db33a85d8c0059086e42bf97ffda2a8c0d80.0":{"name":"@sberdevices/assistant-client","version":"1.1.3-canary.47.c977db33a85d8c0059086e42bf97ffda2a8c0d80.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c977db33a85d8c0059086e42bf97ffda2a8c0d80","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.3-canary.47.c977db33a85d8c0059086e42bf97ffda2a8c0d80.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-d4D+sjYkM9yXjoVHyA3X1Nac5mep5xzhlG2Jyao4KXc528swvJu3YHMebyavsO7H/kFsdXXH0/PN2BvW2kQiGQ==","shasum":"efa3499a521c673a493ced2873f7976a390ebaaa","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.3-canary.47.c977db33a85d8c0059086e42bf97ffda2a8c0d80.0.tgz","fileCount":60,"unpackedSize":634621,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfsjMkCRA9TVsSAnZWagAAktkQAIjFcFS+QxKNjzOJAwBZ\nNKCw9VuwxhzPCbyjnhe+qEFfUkkkLWv764V0jnPJQOpl3Q9Om2iY2yj4OaOF\nYRyR4VusKNSYYfZNFB0Snc8/9aCAlMo0ySH4mgj2mnYDk6Gs27x1V6lcpEQE\n8se60msdvDn7BO/Qb3bCVLHD03mPCLIcVIzox3+9cpiVqIH0mXA8RGWHusKn\nqOC7Fs7M6Ge+JwvrO26OSlatXuoVYMFcSrPqoozjT49Nl/k9nIzpKxv90zDx\nd/I7k4RpPBklw/BVcJ+Bz4diMvr293jkMn8HnbI63W/1mx/zWLSIVt99tMR2\nQPjKJfeUUC6rEbRWPE3Ppi/SHTnXJRfto8MvS/Izl/pVQXdIpcJ3Eu77mr/M\nsM/qrRb+sl8fH2kruHAV62Q3+sBPa+J6UaG6neWfIU4IURzLKQ1uKIrY5WsE\nTBtg1juADVN3en8T6F3krMGWic00Hy2crEZRNQ5AdGPmXZ1kegrk2kix90Bt\nBgz+5rLs3rgGq++WO89F5lCBQmaLhfA1N9jevKFg3AQDXYB/ZwqvMpNaRzsL\neGtD/nfKGU2yCA7XgwLZ/KooB2hUPgyzG5JflZ/D3llK3fNmhuhmhEQQjQOs\nOLSUJWCsp5+8xW6zXtDgY7xu7mxgNBeNBAC9xRX5PcPsRqvlN2ymsw9tAsZa\nkJw2\r\n=vtH8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEMCH37VuElPivG+ldtPUHbTTq6AtepeUfy61eTc85aKk7sCIHnigab4xQcpVoj9YyKtVNC+lU+zer75/xUAzCgn6PT1"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.3-canary.47.c977db33a85d8c0059086e42bf97ffda2a8c0d80.0_1605514020226_0.03153994373231095"},"_hasShrinkwrap":false},"2.0.0-canary.47.36b72a0145633e6dd0859a486cb4afe4c660d3d2.0":{"name":"@sberdevices/assistant-client","version":"2.0.0-canary.47.36b72a0145633e6dd0859a486cb4afe4c660d3d2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"36b72a0145633e6dd0859a486cb4afe4c660d3d2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.0.0-canary.47.36b72a0145633e6dd0859a486cb4afe4c660d3d2.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-ldG+DBuzg4FKaZehUUgEey+SXvx1jkEo0aj+YW4fucVswO+igDZGWCJkQ2mmMnHj4BcZfWIhllcWvD/TkpEbaA==","shasum":"7896824e1453d1e60f622a2ad1c53e60c61f006d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.0.0-canary.47.36b72a0145633e6dd0859a486cb4afe4c660d3d2.0.tgz","fileCount":60,"unpackedSize":634621,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfsjU/CRA9TVsSAnZWagAAURMP+gK9FkOXBfh6qcur0UVp\nAqz/8wfu/tuNjRcqsdD+KOaovIReJzy628rdijlOJ0jUeOi1CZQn3DblZVjp\neiDiPtXRcJBoyZhci1A8pKqJ3GXMeiK18O0dUncQ2Jicl1ysPDbixn3svnYb\nIM/yLg3P/NvFnEUCQetvBa4NtVDW9BdFE+Ab71XmKwQwPX+bBdUa6xgZRWdZ\nTR6EUFEYrUs+0IF9Gd1nZjrGWyrBKBunngHSKuwiVmbFX6N38G5A8+Byk7oI\nbr0SdxtKaTGEbj8hjzY0ccURft2zp7b9dntqt3NxYnVzKDPSlmBlG8y4oavn\n286D/yKbL7GCdCXjHeNTetvYATFe3cTePXkqjZGEUsy1gWCEcqgsBZzXsCH7\nBsbCrtfBt1mHF20P1PyL/mISlMmppPdDSWc9AlpIrLAnareDA36pnxUhaZaJ\n3AYEg62Ta/TSM5S8Mhl2cKYZtBZH5q0yjvXJ6FizY++vIpz0Ih3I8brTrGJQ\nNNg0xVcHhWxqFNNXUWmpG4OHK+tJ5Ykriz1pppX1BOQejHXsmWNOu7fZNt/S\nm5j3SFUJfPZnIowh3UnJWEOLQB9u5eG/xc8g/sg4L329c1xwk1KIpJZXDHOi\n70Kxd6CLQWZfhve9PVryzw2hBjaTl5uHvglhKAw0El0O4QDrhFBtCaPpu1P/\nFQAL\r\n=b3vG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDzLbHK9rvoay3oUOzwKSutJxfht6/ITJVqwRAeePvNkAIhAKHz/lyr8m0R88JAO5jTDGskq+RF4lKjGjprDDLWGJKu"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.0.0-canary.47.36b72a0145633e6dd0859a486cb4afe4c660d3d2.0_1605514558461_0.35537428737397225"},"_hasShrinkwrap":false},"1.2.0-canary.48.5300a8567f6a164f0926452edd09df73907cf1a5.0":{"name":"@sberdevices/assistant-client","version":"1.2.0-canary.48.5300a8567f6a164f0926452edd09df73907cf1a5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"lib/index.js","unpkg":"dist/assistant.min.js","jsnext:main":"dist/assistant.esm.js","scripts":{"build":"rollup -c  && tsc -m ES2015 --outDir lib","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5300a8567f6a164f0926452edd09df73907cf1a5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.2.0-canary.48.5300a8567f6a164f0926452edd09df73907cf1a5.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-ynUOPxr3qCPKDpDfuRevqMRJFYMSyz376f4Pkr5+gjsE73mSZ+7mo81xOz+AkJ2ifWjFKFzj2b3NgN2p/U8ORQ==","shasum":"8b4d712d2fe3ab9d560534625c7e51c251e69d68","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.2.0-canary.48.5300a8567f6a164f0926452edd09df73907cf1a5.0.tgz","fileCount":3,"unpackedSize":82186,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfs5NRCRA9TVsSAnZWagAA3aIP/ixYWSo3+urtdeODa4wV\n2FoojUpI4yy4qOz2blmK2KxyO1mOyvutvOGoC3669751+tUA+QdfaOYBy/zW\ngzm37ExcRsSZQm9F2o4Uf0wJpj0xMOAh+rMM/QqjmxLX2GQvYmNu0z68QdVm\n84yCiWvSOt2osW1evHiQy1Kebfc/6yfY6XR3/VvI2z9Z37zIQOaAoxAUKXaK\nrDfRIQENQKO3hnL0YMfDTORoagBqY4QdLwHlm/Z/dyc5ictfOCfKhZaDHd3p\nKUcvBG+3Dbe11cdeEvk0vSQtEmkDlNAusEacxK8J5HoR4fUfs2ZXMpVmQRjr\nDG5zhAqo+TjQk973hu8rMq3HhisAqNYOHCB8yKjUGMwgt84cz4aaPYgq1l5c\nfL6rO9YdfqQHI7OvVyp33xGxjO6/pg4UMvyzOYJeCq3ntcR5cGYfnUgZsGrr\n1sN11cW0ZlnjXi1iV4Gq2gjGbhN5HsWb13j/mEj3DR0Kyc5uBg5ReF6gQqVh\nlnzeEtaH/1MgBU7uaZJUIeI743YL3GnnkdP4iYtMdyBEOQwnjEhNf915eVNl\nxZyHGb4gh6XIfM7lxVQWhOiR3OSPhCIIjgegc+1m/BdnvmOU4cZqZAmDZKc1\nVIOF+xQfJMJDyHFXvwXS/bwsZ+74taFIiJmsy6UDz1L98Ahs0B9ZPWeKp/hP\nC4bG\r\n=+mmc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA0Myl3lKfYy/mHEDIOktKI3nq5V1yi5UoizWqVTGDfoAiEA7TeMy/3lP8+2loUS0xwtlieKpT/DajOMTvP1VkTHHoc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.2.0-canary.48.5300a8567f6a164f0926452edd09df73907cf1a5.0_1605604176943_0.518413457662408"},"_hasShrinkwrap":false},"1.2.0-canary.48.06759b70740bfa05ae3b420701b5959a5bfb7b69.0":{"name":"@sberdevices/assistant-client","version":"1.2.0-canary.48.06759b70740bfa05ae3b420701b5959a5bfb7b69.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"lib/index.js","unpkg":"dist/assistant.min.js","jsnext:main":"dist/assistant.esm.js","scripts":{"build":"rollup -c  && tsc -m ES2015 --outDir lib","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"06759b70740bfa05ae3b420701b5959a5bfb7b69","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.2.0-canary.48.06759b70740bfa05ae3b420701b5959a5bfb7b69.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-zEjniqEbQsuUXXsRlnUC9R3oxeJU5nzoZYqqu98jLayTg6qFDGXxVQT6ERQx5UkDAkJjIZ00RX3QySyzONtE6Q==","shasum":"240ae27178fe337413c33a993eec364349558abc","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.2.0-canary.48.06759b70740bfa05ae3b420701b5959a5bfb7b69.0.tgz","fileCount":56,"unpackedSize":1508171,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfs5acCRA9TVsSAnZWagAA/ZgP/2EDuxSWG+XeFDKAZ2Rs\n7gIfm7m6yBm7sIrH81YmXqrosf/F91qlAe45rqMgmwRgaIEZTwO6Mkqi1DqU\nKtF8O6K7wJJNgkpq3+n05I1KQ4hTuAyJlK9UrFp6dZU1IRP4oSj0t2lJvx1n\nDO6NXNFrkTbAU0Frl6Ct49mF2nhPbwbbKdxOKJIAT89+2XSlZU1G7x6qjud4\ng6XNiFOwnAMlErx2+Q9jfUQzHOIbJtR+SPRFPg0NPqnhzBeUzCmNWeeGveH2\nYbGdQmAV8LRKp+zif78xMaDqNNK+I3yY6rYO4F37ObgcTjBcgu1TdZNk60r6\npfDrrNyJAVgvUSC8zzVijt7dbMQuq8kk2i1Te6bphP74TYs25mBpMeWTESUC\nKGjPS7ePL9jm4ekDVLhI3/90Epnl+BF7m9wKNn67RzUqqmTrBEFRWILAxGMw\n/V9OftcQgC+icKu9sJwSQzu4iBMJzPZ5gGLa3WxQJ5R4C3ssmagiGqt+YUmP\nQvO5cKGG5AmaT1CAqhlGL2dlH/Uyd5o+6aHkKIm632z2oGdbbvfATPSddr9Z\nV4DfHWEl/IlTPg3H95T7Zn2EMINQv6h7qkgITe1amSEx6wWuzuq6hagHnDwa\n84NK1m/KlT8vJvusV+6qh3MDh+ulinmm25y5v3k1/rmmVO8xOQdQlV2tlsHw\nMEv7\r\n=r+LR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDWdn4SWDDO2NuZ6JMU0iXpe0HECfzrxzIyR7b8NT+8DQIhALTUppVe+5mTrnI5GeEqhX2ciBKLNIcdONh6o6Lfsjmt"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.2.0-canary.48.06759b70740bfa05ae3b420701b5959a5bfb7b69.0_1605605019612_0.9423354671751081"},"_hasShrinkwrap":false},"1.2.0-canary.48.2d58b78418ce0d9f4ec58383bb4a9eabebfe402e.0":{"name":"@sberdevices/assistant-client","version":"1.2.0-canary.48.2d58b78418ce0d9f4ec58383bb4a9eabebfe402e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"lib/index.js","unpkg":"dist/assistant.min.js","jsnext:main":"dist/assistant.esm.js","scripts":{"build":"rm -rf lib tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir lib","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2d58b78418ce0d9f4ec58383bb4a9eabebfe402e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.2.0-canary.48.2d58b78418ce0d9f4ec58383bb4a9eabebfe402e.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-RrbpfMTugFYoqVuSKoe6I0hhnV5va/K/df+RND4miPOamSY8FgTdfPlj/cFCKTYwE907FF9MME9wnI76a2hQJw==","shasum":"d8d999279181f0af6b493b3d3e12fb23d7a52ec7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.2.0-canary.48.2d58b78418ce0d9f4ec58383bb4a9eabebfe402e.0.tgz","fileCount":56,"unpackedSize":1508205,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJftMdwCRA9TVsSAnZWagAAZzIQAKCW5wizDiC1PlPOcSMO\n5MnHmQTDpR5nxCU4dYiFfPlFqBAnZeuVblebovU0WvKNxAUBiDsVwhNHyGv/\n64yH2c/rkSo0FiYZ+lXTTviyhEd03QlUeExl1ZqhF0j7gAKBmPtOpRKVOTbd\nHR5fg/8AVMerA9NQBGXvjbUeB32yTYb+WwV8SZVJh3vVt3D9uudx4I6EF2Ul\nDUmZVAkDhBGk4svXedDiSC7lXSE9dxSeriROvEREK9T6+iOe9JKVm7dBhTOt\n/qH72itSlSTFC2SwhhdLTira5dY2wo6Mgr++C+1xZRdTucQ45ZJap74dYCzv\nmZR5LNxb8o8m6qI4vc7CIBDzPAw4OFmrdL0yS5UKxvNrH23drqMBN29KbZyt\nVoDzKQo/qVahyR2oSE8UCrrAKQs5OgL4UjfQAXerh9SuLR9XzRmIwHHP5Rrf\nxadWZJTi3edePv6XCVJpCQEUL8B4CyI3CGIbGb490bH9vYSMBkZCoav961wJ\nCTUs3arlMrGUPfnlDB8jeMsTuNZbI3Mk8P0IwnVc0c+uFKVS59e/275oFwN+\nWUvFHat5heE/LbIj2css7+VfuNNYV4ILtrw93nl5AsJ67gojNoPlxErt6ALV\nFp1tLV6Y9gUB6lAk1u6y5GcIfNMuKtr4FK13n4aORewOcgtrx40nlMLcN0k4\nAGBD\r\n=XIdb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD/COFesOEZlTMortc5MaSZX/woxx4L5kbGu4QNRMe4TQIhALLzeKnHCzDI9G/83E2joSX5GrZ99SZ/UkIuQv0Uwnqq"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.2.0-canary.48.2d58b78418ce0d9f4ec58383bb4a9eabebfe402e.0_1605683055416_0.33529382908097305"},"_hasShrinkwrap":false},"2.0.0-canary.47.3f63e50bbf0cc6e0a4a744e6037012680fe05216.0":{"name":"@sberdevices/assistant-client","version":"2.0.0-canary.47.3f63e50bbf0cc6e0a4a744e6037012680fe05216.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3f63e50bbf0cc6e0a4a744e6037012680fe05216","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.0.0-canary.47.3f63e50bbf0cc6e0a4a744e6037012680fe05216.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-qFHEx6R6gjyP/Xmp/NKMqUqU4UJ0OgazM3kBMijk+X9uZx3kQI7r8elPhv4GCvkQQT8uJVasI2jLuv44z/Tp9Q==","shasum":"ba2bf6144151b5226b04fca221450c8935d44efd","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.0.0-canary.47.3f63e50bbf0cc6e0a4a744e6037012680fe05216.0.tgz","fileCount":60,"unpackedSize":638497,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJftkfeCRA9TVsSAnZWagAAapgP/i5wMGO8uXNehRWr9FW7\nKt4gMCQS42xwxx10F5F5Ud06D+XQcenDQ+782jSJ0p8sTbFnP1jE6PO8bZ59\nPByJceHWVN+NkLjjynF7LJVYgouxDhcqhqO/OyfYp5DwP4hxH/iKh0GWGkAt\nKN5mbNOSyX3fMhAEkHEFyDqOYpRKZtGA2ketmEu8HDuuxDUAFlRCtWATsDD1\nTiCGV8rVQ73LBzZ9klsS32PnkfCtj5BIKIEPGk/jfSgfOpOm3cRHMEUgvaNp\nJXF6SdFP4Nmvt+fF4zssEOt8iCcRHyVyOMNSITcFNv5rDJMOZZwDdNA4VWwd\nU9F25fh0KXL95yFTWr/JPxdF1Grz3YHyH6/kNeyPkyNSELRn7aqmMtEuCAL+\n1bFW1jvnlQA6YUKiimiDPghIFf6aDePtLZmMIOl85gIYbAOvqoxKP6mH17ro\nJNT6Q05Hcb7SOLMHtOi13Vx7vY+gT2wd0DlXJ1Ohf2OoCjyOntPz2jUdiHnf\nEK7zx46vnY/8+trlBysavPLNFphMq6AikW+aQtYrGjgtAouaSwLoxg5Byvok\nbYmSS9+NrCbRoYAP1TVSpe+wetbw8qkdkiNVGw6VFsZ80gjr/CHsmMfWnkuK\nGgBVsbwviLxdzNo9hHerb5xUQyCRshVgeq5hR3uBtn8y1jc9QzIr2phM/b8p\nCUiG\r\n=p4r6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA6RcEqbUYplO6046vdLEda3warCXmZL2joG90Mr1cr/AiEA8RNftx4kb7lEkHGVdMpnU7kms1VjaFVAJnLfLGBhKP8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.0.0-canary.47.3f63e50bbf0cc6e0a4a744e6037012680fe05216.0_1605781469827_0.43107888487510926"},"_hasShrinkwrap":false},"1.1.3-canary.51.eb7663ab912681c9921b58b8434133cbe9f8cfb6.0":{"name":"@sberdevices/assistant-client","version":"1.1.3-canary.51.eb7663ab912681c9921b58b8434133cbe9f8cfb6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"eb7663ab912681c9921b58b8434133cbe9f8cfb6","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.  \n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.3-canary.51.eb7663ab912681c9921b58b8434133cbe9f8cfb6.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-+LE/712UOmniIQfRG849jaSlsxTToRqAor+3xTJgmlkRugACIGWxJAhjenQXWGPeJxt076K2E5oSi+KpEqyZSg==","shasum":"6d4c5648a036690e7bff5112bffbbce85f4f7a18","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.3-canary.51.eb7663ab912681c9921b58b8434133cbe9f8cfb6.0.tgz","fileCount":57,"unpackedSize":632522,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJftlgDCRA9TVsSAnZWagAAYWwQAKIwWFdFMqXtd1topfrA\nc7/Opnv+TcE3IZtLRtrEXUSliE2BUJxnThH7YiFQcBvL+vC+9keUPaWhJ3nC\nGRd/AijoVpFDJeLNueCGKc8KUmy5aHJyVBi1uNm94coJrt6Vh9wreyCvpe8J\n3myDZSexU29A0Pj6JPYcoE9KEZv90jntsuRNrbBtGOEc+p6KHQMMnIkC9SiJ\nBQOvRW4ZxdpxUuBXILAlBztJ3WTyNy+4rwFMhTFUMJ6Rtg/HJ5B2SqxVms6R\nc3sZXzPVuk1ruzdIafUMccuWFUi70ayoSjRIxUkbm73cAsgGRRatsor0dw0d\nrZbNMclrYJm5jt0GT47nSnR4vl3l5HRkF9cZXEXsjf3FHBZK5XaIuensbRrB\nUZvhhEdE8PjSKp3ui031aedE3Dr+F8LR3wttMwmiygoNQzvp2OHpYjxLIu8g\nhXsPGzp7P8ukQ1+iWZogKMUXEO9DP6R5fEbheC7l5ikWF1iJE6zVMa6YsFHs\nKrcVPmETK6n4jkhUjLSWUyuUFzium9mWKcRZAdDUhZME5kdxdC8P9PSJ+Ptq\nlApf7he6XnuPDr2NO1yFRuDfxDHh6vIh1E7ylTYiF5NQo98drOVccqAK8D5w\npLbq4756Ph4IQM6gEsbzoj7THPtXtle1rWpSFxt+Kos/ZBE6wlCn74E9U/R9\nNMrb\r\n=PTUw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC/G1VR3AmHpeNk+5/1KwSTczmmjgKpsARmZ8JplbnDSAiEAt/GOlmjYiB/NKdkAtnWzCT0T56NUnquKgFIkMIjSKYc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.3-canary.51.eb7663ab912681c9921b58b8434133cbe9f8cfb6.0_1605785602923_0.5918383651776022"},"_hasShrinkwrap":false},"2.0.0-canary.47.23999220acd326b1b94bb988a8dcd653ea564075.0":{"name":"@sberdevices/assistant-client","version":"2.0.0-canary.47.23999220acd326b1b94bb988a8dcd653ea564075.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"23999220acd326b1b94bb988a8dcd653ea564075","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.0.0-canary.47.23999220acd326b1b94bb988a8dcd653ea564075.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-az8kn02iJ8zp8F7RFeaLdpEt2GZxiOVGR6rpI65PfwJ4BskB4ScTb5GHPN9qp3dj0KQqc80DTiFxlOk7+1sgww==","shasum":"cb192458e584d612f7d4a9da16bfef1600124532","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.0.0-canary.47.23999220acd326b1b94bb988a8dcd653ea564075.0.tgz","fileCount":60,"unpackedSize":638852,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJftmSDCRA9TVsSAnZWagAAeUoP/3fVdxOEzO0VqCu2lt68\nc8jgF6QV/YRE+9s12+TnUcpq8taip3szRPg75fYm+gPVBXQ62XZgA89T2gSN\neuLISPQ/GYEaspkXUNBUKpI47+Nf70XHFlhCiy4EugwXVy1F3SPlKoYI7wlL\nOj1grVpnChPIuOcNu13QabfeJzqvHI+gDCn1l2AIGr1BOle1LmnmibgQs3qU\n1tb+RQYUJXLRAWIG/yZeu8NhWjXMiZhFPiu5L58JhpZwtWJgj1aIBWOfjDHc\nVCyTVGvgZlcY6kqAssYSoMR23Uui1C+T6P/WUs5k2jmMzzI6nqlJzJivrh00\nmjEGWQW0Xll0tvvC8YmNGnYkbcxgHdVAMqd4vUBy8Za1IbyNGh1blGSrOVNe\nt9MTzpcJTUBkicyFp8Uma6sjcKiaHsDQyt1sXlUY28SEXtb7N9kDm3kZkm+C\nBEyQ9T4RGMVr19h3fbB+roKJS7E4Xp/RQ+5AY5JhdC7SG44AtVitLUojiLf5\nj9f+yozI6yrVulUGpqR4ius5yJHwESdA06AgJOVQ3Wf2Y5uIIInsi5EMj1ce\nhmYGrtwihcJ9xBQb/L0jd8HUxYbVH0zD+blZvw8Exo+RWEVcdOpxRBYJlkj6\nezSUISngmzntPjiDAyWAzHPyptDtk6kReAPH6Qa9zbgUJSMaGFWAkMh677Bf\nbwiQ\r\n=n3zw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGBQUvKIq+EL9alTF+mscc1kPUkBGSpbzQXElw7aHrheAiEAt8CFgC03GUUSR+GPKjpSsctY/ip0Cz4a1j2mT4bjOAk="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.0.0-canary.47.23999220acd326b1b94bb988a8dcd653ea564075.0_1605788802585_0.9430360049489022"},"_hasShrinkwrap":false},"1.1.3-canary.51.b066573.0":{"name":"@sberdevices/assistant-client","version":"1.1.3-canary.51.b066573.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","gitHead":"b0665737cb5692ffc3731113741cef9d6949991e","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.3-canary.51.b066573.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-9IUuCOWSzeFCijTJjicW6su5YH0qBtyyzNd1Ey3iwyZQhXNTrC+TzLLEmN9l4bW9BAsSvbJc8DB6UXdwem1auA==","shasum":"734e1ce3c31a355017e3442033a5403733aabd07","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.3-canary.51.b066573.0.tgz","fileCount":57,"unpackedSize":632802,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJftnOFCRA9TVsSAnZWagAA9dEP/0Y6sMVJdS2llzcbeZxv\nG8TeS0JGtuZQl9Q/GZauJVdqJgAghH/vbP/lmcrDlN3+SBjqI6rFYRPNy8oi\nO5vXDoKxz5jTYvbjn4kjH6Hmn4NrpB9ETdBhiefviSWkZzMBV+mMN1KkBN0u\nr5TYuA5qeM3NAQD/gb/PTnTlgs+WA9/KG4NqrsA40r4b6GjwBH620ZhHw+q+\nEtNmXmCPdZ/1ct15S/vYm/82G6Z1JOH/suzU0mcnNlZuF8mq/0NtwWDBM3BM\n7bhdVzH0cCFtgi0qCHK5Z2V4E3vfOlpNIxwUINhSaDpQHaGUfn43uSvbjaBd\nb7Sm7LJhrG20l4BBO6HBzVUvOEOif1uc5uX91e5Y6pszIgaKEKJNOaRJcA2S\n5onTMIaSrd+UFrn09W4k8Wrhk3J/mch75uOFWbJ3oGXgE7ngrZZtVim8N2TM\nVp9DYIPOrqDt7CYIg/GtgBPTDGh8XJlg/FNjVJfmrTDP7qAg2KcWGiwvjuf7\nir03/vJytqxkbYjexDohrQh2l/6TYfJiP59qaqTk8vLYRYi0XvjPV7NAjxTj\nKzNHAa5ZeZ/dus+C3Cov1CoL2yLMDNijl98VZHV6AjhMiGHRQYeFVbTDX2Vv\nyyscMdkFg4Msp7TuHyJ/hhZMLGKUtjc2pPtErKgRkVqki5NT2P4r0MPo0j/I\nX0fm\r\n=DXV7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCGxxAEasvYcljo2NU/xA39A4jcBGkNktgIj82mt76DgIhAIiTnjVox5KH642SGtMWdUM4vhV3DltJBZv5scQbV3EK"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.3-canary.51.b066573.0_1605792644946_0.5129660712868653"},"_hasShrinkwrap":false},"1.1.3-canary.51.b066573.1":{"name":"@sberdevices/assistant-client","version":"1.1.3-canary.51.b066573.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","gitHead":"b0665737cb5692ffc3731113741cef9d6949991e","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.3-canary.51.b066573.1","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-irP9ypfwFE4ykQBqOUzk+x3fQuEgvwKrbK5Jo+UdKWNBXr53P+1l/v9FpfYdl0aesR/9rORzyf41L9k1pj57GA==","shasum":"c51fc178b3daac3c48aa15ba9c45db2fe01b580b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.3-canary.51.b066573.1.tgz","fileCount":57,"unpackedSize":632802,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJftntlCRA9TVsSAnZWagAAUw4P+wdf8/igHRN2BEIRkJjY\n2hokbvc2uSREaNN70xxH0+1pYWWdhGQNFv0fEJ/lQjNeW8E4dsyQnTclLa4q\n+Hgdn7IgIQzvfr6qVqn/275ndeZNviDwdFcpSSKbUMbytW6yftsitZXP48j6\na7ARsHVgYmsMslGF8oXApFLtGRCwdsjVkTwDiITVimZJ+3CYyJDoCXv3T7GM\nPz5FfdNq8FttR6RqN45+24amY/qK/SsZcEqSbOTcYAb8zcgh/BcS/iET+top\nxCjL4Wt1vYNV6ZKIbFwghYnFxSgEnQ1NyDks1498ILxQrsacMwIBDx9z8b8R\nbjpX0vuWkjTqWKmj3OtKYTFImlvEayS58kfTY+l5hqU5RnVqYp71ErDuH0s7\nSgGLLzdjSXFmRdf6FohcS9PE5tXwvWA7+odOdd9UjIvSZE9hwhmy26zevVm6\nRH32IvfwuE6He3FIfJhohYqhU7pYud0ueRAe9KSN9kFp3+ImuoXcYJ1tnENH\naX1o4MoRXp23EOftVeJM6nLLxPv5k2WkDLwG01/ybgCamSc+SnwAAz5712C1\n+t3IGWTp8VQY53aDtNKkDdHHwFqVM3OtVgQuQGl+C646C8rfZjQxAO1ehqbH\ngBQCxa0986Flw6kjX8s/63+TCCboL4X3MmSj7AbycppgSqUuwH6NiJFlQJc3\ntXBl\r\n=lKxg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVlpU3tuZ6TtkL8yLJrZ4KYDtB3tD7U6q3a0vOZ9Au9wIgaRGrMVRKi7ckf42+EvnBHe/Rd80uxwMV8WE9/RWzD7I="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.3-canary.51.b066573.1_1605794660747_0.9750287563417448"},"_hasShrinkwrap":false},"1.1.3-canary.53.fc82d53e50c23f0fcee8a894468fb30e1f98fbfd.0":{"name":"@sberdevices/assistant-client","version":"1.1.3-canary.53.fc82d53e50c23f0fcee8a894468fb30e1f98fbfd.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"fc82d53e50c23f0fcee8a894468fb30e1f98fbfd","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.3-canary.53.fc82d53e50c23f0fcee8a894468fb30e1f98fbfd.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-HGtdNt71fER7sk44RQnR9gmgVhXUdJwfcPDNjXSq1SOtumszXgyv0Y1METQZE9MjBvxUeR7uaAYpy3kkQcc+Fw==","shasum":"c5f541315a3d79bb37fb5152584a8b3860765245","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.3-canary.53.fc82d53e50c23f0fcee8a894468fb30e1f98fbfd.0.tgz","fileCount":57,"unpackedSize":632865,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJftnwpCRA9TVsSAnZWagAAGLoQAIyaUQxEpUCAU2n7M6QD\nQrJDNoqiKL4Qq/Nsnmp8AB15bwQh3V8KUHJEvMrlAlDgucJ6asbjChIelNAC\nYsagEH83UdX6R+fPR8hgGvgkSIfqC+xykboClZCEH2aUzOPnt7WQDvsyLE9d\nauRlKl+ZKvzXvm5Gy6PGLXE5vYngHPGnqYn/8powrO2+zWkbcuEJes5x2rO0\nOtAugfCWqNR9Ctklc20ytaErfu6zMvlZZkunsXoiwS23WB+Ffq84ldXxJ2fr\nYHnKpUshhywlaJ72OvZkdP3FaAQb0TIaFfAUzEsHiobMuIU4ntQfaqi1m+jT\n51zzCa08cpej0H2Rnh8qAQ1ZheJ2g9eu32wSOEbfGPzqjs3qbJ4X5gjVOHc1\npqVy01VL518Ye+5t8Kb5YY1AnLbXdNDYABThQ0BtxxWiCdeKQC7auK+dhyZx\nPWaHiQEhHS4HZuhZGP/5r8d67Fei7zLPD0bdA0Neo4q/+4/1SjdxCwqvXcOQ\nHgIu2lsXiRbpI1EbxPtpe7gKo9+ih0FsJ93Y4pN5r2KvvoC56/I1jbpW8su7\naB7iL57Y9tl+bGwHy1yWIwe2Lfp/jCstIQmjGzfPeNXoxFUyLy9yAjg5n+iM\nsf9BuJHOS0+Uu+gkP//Ci1+taQvoaVlWeZMcDmqm5+5+beGd8XT27h4mS4N3\nVikw\r\n=5D9s\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBFiN7djJSE12I/570cF3S/RK8z11WTPjP5BOKmRPK0vAiAFNBQ5o6zL6p47LaYJJ63t6yKjkG8cjA7oNcsezgupWw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.3-canary.53.fc82d53e50c23f0fcee8a894468fb30e1f98fbfd.0_1605794857015_0.7770047842498322"},"_hasShrinkwrap":false},"1.1.3-canary.53.e4b7570f10a82dc107b62b4222a1c23b1807552e.0":{"name":"@sberdevices/assistant-client","version":"1.1.3-canary.53.e4b7570f10a82dc107b62b4222a1c23b1807552e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e4b7570f10a82dc107b62b4222a1c23b1807552e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.1.3-canary.53.e4b7570f10a82dc107b62b4222a1c23b1807552e.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-N+WHCxjI+ILPnxur3/j0yGqEdRVUIjsyeh8ZCaySwJRVBzOJLtipIhgtnYV+b2zi51iib4QHWwYMFKZz0SmiSA==","shasum":"dd529d604f330d1612d017bae70150c05946ee91","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.1.3-canary.53.e4b7570f10a82dc107b62b4222a1c23b1807552e.0.tgz","fileCount":57,"unpackedSize":632889,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJftn2vCRA9TVsSAnZWagAAnA4P/0L5K0PAXozO0ByhbNJG\n0lzVdlPJrhr0PPztqmkYjPjx/+boe82n9efZjPAGcObTcr/6sXNVuM5ew4sI\nj8Xb4xhDiMUrWLDOlw5zeTNybQCbmvv16oaEYXFpT7bHxlUezsp47ZCX0ESt\nEoI1Jh5onbk77Y9TnwFuvRP/lILreOCgUFWpwANSVM1+VPn4PBqEHPuvMTHV\nbIYyyTwfJ+HUrX5YHNqMCWPGYE+Sjd2pOoofk2CulCA3owBDEn6jMpwomOvM\nvN/Y7tO9uKePD/v8RceADsXEav0ZYYKZUMzwvQo+aZYk7tU3ESnz3ZKTb9Py\nultMR+djarZ+Cqaf0dQw728vZm2iX809QJPh+7pWlOqWjZbDAEaTxIaBy63U\nRfV9vSFEUW423HSMuIxYgJs7GjUR9J4kEUemyLF12zN7nn8v5Mngq38iY+4S\nKekTQSSw9KzNQFu96BbR7ZdFD/TV7+wBx6e5V2+Z0JDz5TvmNhIQQ8Bqh4yh\nWYrof8tp1wTgQp+7YJoSw41UtPNmEGjz2UUNHjPCh10OHtkWSQLwXKKzDncs\nd6HsEtWbYocwag1nnyj/nMZRaNwxD/7XQxI/4m4XbNL1mMP+77GV+ycoVX+j\nzgGtik+GNucrrzQq5vIY7ImzL8pVKt+ZQ9EwbOXlx2j9LOTxnST2TyYmaQqv\nvWyX\r\n=nKNf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDh4NB63lhPP/jzWAyREQy7Af9kRtdljh5qcflSPM4W3AiBkOXRuNZQNtw4oQ7gKjertTeK9pwKbLnC211xp1FeBfw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.1.3-canary.53.e4b7570f10a82dc107b62b4222a1c23b1807552e.0_1605795246811_0.0637005485526243"},"_hasShrinkwrap":false},"1.2.0":{"name":"@sberdevices/assistant-client","version":"1.2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"584562d1923c684808c0ace02b022ce8277627ae","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.2.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-Y49WhIcWJfLRxTkvSvWyAaV/Olh3+GCtwwkicth35rxYs78K36eAeGEAkwXOOBjnNNnYfOvsMigEgr8CIxbdCQ==","shasum":"1033df9cdd6239c63391474bb1db33427dd1dd0d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.2.0.tgz","fileCount":58,"unpackedSize":634541,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJftn5MCRA9TVsSAnZWagAAjsIQAI7n+GUXgAFKixWDrHlv\nUspqQxxRtBChj66mwMyRJHpzMQSr/Tr94E4nIme5sDZP5Zyj9hjrl9SGw6RP\nbOBZjX2GuFv3jPw1j6YaM28RESqJOYQAmkjceJYyIYvsloHUCakYaVa6+YWl\nOAyuUn9kuTwYH5S/PgdOZusxNYKpkfyj6ejHREudl+XG6SJzzjsz3cqNUbiB\nINstZ7MkHBstfzG3ENiWAWOXXpCGXmfESryQHWzoOaMGSlNbOkSludvvxnm1\nnLNhjkzeEYqEeXMwpjKsg9XNaV/zktwuRqnsBTeFawWDVTMrTtHqyjVmxlrp\n3Uv9pgAofMfd86Lo0sVCjojlakJ7VzGLBUTrN+bHVopAEFk1Grzb1Y/OlDtp\nNO9usNVR6tI3vnsFZAJMYzOgHjTxWj6k8Ky5d6Fa7y6ShCQjS6ZBs3tU61KK\nQI4YBSqm2a3g59MOW9nov0j7MUylILniExL6FWCCJ3Kv1XXvIZm12pwwnrA3\nwUozgmi1op5KgODC8ZPfv6ILPvSHwYDP3WoBQvgYV95l92CX9fKSaaVwipdy\nBWEoO51FrN58kVyjKMnUbiicmmwnsdY1IIxslj3+ZbEg0HmbMN4FYuC0H10+\nstNr3LF7KhBKHp9bMeeSbUsMmu9leaP0bI0Hs1UgXjV1LQNTNwLBZxVR4ISg\n2S61\r\n=YQvq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGZ+q6b3ANSYJFYTxI2tCXbSLo3BZHiuViMY816quzx2AiEAnRTQw8jqEwt+jOdfOhMLX6e05sRuySOJ3UXrVidVIQ8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.2.0_1605795403621_0.7576615562040525"},"_hasShrinkwrap":false},"2.0.0-canary.47.8d47dfb7d4df197505de691b2762d483cddcb538.0":{"name":"@sberdevices/assistant-client","version":"2.0.0-canary.47.8d47dfb7d4df197505de691b2762d483cddcb538.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8d47dfb7d4df197505de691b2762d483cddcb538","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.0.0-canary.47.8d47dfb7d4df197505de691b2762d483cddcb538.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-jG6wnSoLvk4W0+nvcQ9TkQxj8hzQOoZVAPvpX1ycyMyn8C8XJzTl97bc3/iZfd9JORsVs3osIsibYPUq/FgpwQ==","shasum":"55cf9f058f7c5b90b1e2022325d6cae6abdea76c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.0.0-canary.47.8d47dfb7d4df197505de691b2762d483cddcb538.0.tgz","fileCount":61,"unpackedSize":641024,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJft5gfCRA9TVsSAnZWagAAx2YP/1ZF+5KE65KV/UvWfnxf\n1lQ6Wz0u7OEVRQR7mzGCzOvJEx5PdiyAnxs4axbhYxJ8lED14xzSBqMO+WPi\nENcOAe2Mx0V1x3vlbdgDF2yC9de/5yA3xZlBe3J5o+pGxn+MB9JiIFJDV7P+\nNuFOoMHs47muuqPQYl0Ie+60qUvyFWravDqdHm/19K1X/vgtBGpsaHMtFIZa\n3YtU0Q7DkEJF4/bqlh19ZEGJK4YWftrsjYtojAKTMt1vZNUpPdFMZKrVaRVe\nyU4JeCClesm77pZ5a4dzq0nqQzRmrWxm0MxFrLa/iLo3Gbygc0YpAQGCvbu9\nwopnN+JtbWBiGu1MTEcIJprfQqlY/LmB4xjjqSM9ju8hENAelPtTG/P7c2LZ\n1bzuo07A7qcVcgfZQJJSqgDQ+Qo4wccDeTXBshWugmxT8g82W6bjyNTuBoFc\n7XKCe93qa6yQxCBk2qTeaONmw2YLwI/GDcKIX36Ej0FpwhMBhCGMIuZQghij\n+jrDa+seY2kIaf96qOynch4aynoYJdhXEKVjBSK4RAfGktLok7AsrHkMy5jN\nNBDNA7Q12493Owu6wA0Wvz9rV5nNsKu2RiC8KSZ3iUzmod729C1bx+XlEgjq\nI0ouPnadp0CXZHJ05Ejiq0APZR41XO/ox6ti/TMcNvatigTugAK/6lZCPM5T\nqyXg\r\n=Nfjl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCflpMIhaHml5494EB9wqG5ziW7gpTUX2QnIZ8Fsm6pUgIhAKYhFoZOrGkooCQLX6LiKF/uEwSY3Q/LSrRAEyRGgvOl"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.0.0-canary.47.8d47dfb7d4df197505de691b2762d483cddcb538.0_1605867550744_0.43792189831978656"},"_hasShrinkwrap":false},"1.3.0-canary.54.784dfe7defe33bdec6485882e9dc64567512bd5d.0":{"name":"@sberdevices/assistant-client","version":"1.3.0-canary.54.784dfe7defe33bdec6485882e9dc64567512bd5d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"784dfe7defe33bdec6485882e9dc64567512bd5d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@1.3.0-canary.54.784dfe7defe33bdec6485882e9dc64567512bd5d.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-vvUHe/nNnyL5uPbUr6R5ioGwbKkQ1t8eBa0QvJZOAo9r+ui+ipgDZ9+TxSO3b87ZxoZovVU+b+/92SL55Mu5Mw==","shasum":"28686be58bc0cb3b187baebe461b5439472c5f97","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-1.3.0-canary.54.784dfe7defe33bdec6485882e9dc64567512bd5d.0.tgz","fileCount":61,"unpackedSize":638486,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJft8zjCRA9TVsSAnZWagAAGhUQAITlzsYB68UBSbkApM/k\nx/geusSSXJ645Wiur3FsTbguBj7Y/V0SJGDbfB3LxdgFM/N/9oB0U24x3fFL\nWoAE4ON+nGeNshNwcXTkwPNCtXVzZPqY0wNCWjba2MXqf3AlUeLKI9EH9CoZ\n2IOAEs1RVUU2BtcJ2xG/W925toJaAluUZcm6VeO3xNUTYyYC4Yb9RNJ6XKi5\nbsO+oWzjB0Vn6AC1GHtIf1jw0kq2Np++ijmNTBSimpVnjdBw5TXQYG5RU16J\nrWRlmBd5ZkUbv4clVqgUxcITVhfhOhO3Q1paZEvLAGXa4nQ80EFffq52UO5A\n62WIZlTBmUwuWkp0jccLRvfGIhLGXyiyIk9/aa/B83OSUN3ZD4HiB9OruEvh\nFJ8aYPC4TriPNYD6xRgOhM4mVqvQJPHT5aKQ3OG/eM1S/2qKQ1642MsEYV6H\nurs93LaMfRTNQKPjeSF1yuzX9xq7Ogl8OqMUW0CnR2mc2CXI9x2IE5mLAXd4\nqXl/rRf7QTNYwChwA1ImmNrjQYbLKDetH2I7DnAbRSz4xgvBCrV5ook/57FO\nzufv4m+TWeYRWhy7X/MK4qsOfuDBaBblIpk0/qvSOeH3i2M2gLNSRTmdc1IB\nAxCUqptGJxgc+FAngL9F4zsWRHRYOTxTxULLDZ6C6G8jyoxlIPhxuR0FHN74\nVsOb\r\n=6sIB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIARt9hVr1PQdddGiENpRIGfi+KSzMgNR+LvSnw9E7BC7AiEAtV+ukkJv8nve+iwcbcj3gn0mfIuQckI5YKS7QnIxSRc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_1.3.0-canary.54.784dfe7defe33bdec6485882e9dc64567512bd5d.0_1605881054505_0.015780357486795182"},"_hasShrinkwrap":false},"2.0.0-canary.47.c89de6ac5a5aaf33bb22f5878af6e2b5d798ad78.0":{"name":"@sberdevices/assistant-client","version":"2.0.0-canary.47.c89de6ac5a5aaf33bb22f5878af6e2b5d798ad78.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c89de6ac5a5aaf33bb22f5878af6e2b5d798ad78","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.0.0-canary.47.c89de6ac5a5aaf33bb22f5878af6e2b5d798ad78.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-VXvZzDyJLuX50EAM+7kQrW2jT3BNPgyTiYw2iZT6oT2onSQ2DCiaR4UW9LcWmh2XnQ+xWB5GIf31KcTyYLaOTg==","shasum":"d3c7f01d969b859037a70f07d2419834eef3cd17","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.0.0-canary.47.c89de6ac5a5aaf33bb22f5878af6e2b5d798ad78.0.tgz","fileCount":61,"unpackedSize":641024,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfu5YVCRA9TVsSAnZWagAALDwP/0YN+txnqzPUasj/qOYZ\ntN6iDTzHca1p8kqvmRHItqcY/9zy0+bVy9buFBq2MyKhaa/SL4GN6wIszUQs\nze3Lny22hm93jGPdyo3pAv003QAvpjQiAcrtNUkujwq5N0V2ajIXZf1jLDVF\n8pFV2Qyik7NFqYtaqlSYjH3PJ4nUCvZPbTQobVd9LAT2ViUcwvkdwr9cRJMM\ngempSFxVgvxTJI0Hd13jpvDI0aaai3v5BYOx+rIH0S/Y9YaelV4AU+LXa4bx\n86aR/KuIOFgilR9eblBmMCYJY7qOctYqe6/DT+i+53VYd1yc0wgyK9XVUmLX\nJvZAWXcGWEpxeI8/z9pLKROU99JpYgIcCTmJe2O5UOmaTPj9nTbW0WofPpd4\nCohImnF7FeNNpG2R5IJNBqlaknFAoHFqeUETJKyOGiR78F89FHqiQitUQ78z\nfZMH0E5mMB0PxLyRJbSERMxwLiUFMY1C87z68vi7dWC8m7KHn2X6cYpo2+yp\nozwNkOyg/FCE4IKPN6Ii0qljDRlSbNkWLLg5f7tOMa3kHanUKXMg95sRyvuw\nQ0QUsT1o2Pue3RdD+tUcm5Sg5dFynHKuKuN0/PcZ3tPIislr22RXYU20UohY\n2OZ1X7WMKxjfiBYJA5KX9xDG2BbBsW8PNRVv7J9KMkhwIkw66c8A516Wk/TR\nfMnC\r\n=/yvc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFh8j6A4Bpo0zNRR8ZAWKa+jALebgPlF+B9O/NB69zVbAiByiw+BuLPghMwQCA9xWj1kvanrv0UcDYTmeqIfCT+Bdg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.0.0-canary.47.c89de6ac5a5aaf33bb22f5878af6e2b5d798ad78.0_1606129172602_0.16676613820738462"},"_hasShrinkwrap":false},"2.0.0":{"name":"@sberdevices/assistant-client","version":"2.0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2b55275d72ff4089f136f7c79132541ecfa2fdba","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.0.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-iYYPxhMAnLrdj+MG5W2ekYc2j7FsWCLkAy1HPVxX7E/64YggLrnp/37/l0VQ7J9+D97Qj2OvWRD60FyAN742rg==","shasum":"6c8a437c743f02ef8baf3a8e9fd180e16c64f7f7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.0.0.tgz","fileCount":61,"unpackedSize":641306,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfu5aqCRA9TVsSAnZWagAALrIP/1FczfMcB+c5tCdOUZnv\n8nMprw1X85ZcgvXAOGtsl2SOyMyeYJUhkPueEMpD8/yj8f6RYhvKiqTE09//\nyXD5+B77XLO+tlpgeEXUQ++Lemd7YWIspnKPYKvLAOiRcNZt1swIjTrVNxnT\nkXmjO3hPVrT6kNti1lPXEOoOHNTOrRD2j+tBn2Ccn5uMQrZ+hXxmQrC+3gyC\nZiV3KIFBLIpUy51nKEiQSP0mAOU/1qecuIOTBxvMmlTtSeU3nj9MzrT+poEn\nFhrntFJPz/e75coIig0en4vmq7JlZEvS+kHbHhLq37Kn9qfrlXhdFtcpjgrO\nL54QRu98AdqBMMv5FS0Xl4TL8X0TWFn3UJxNWK9eZ+y70yY/1EZA90P1loAW\n5+xvMTc8Go+bViwV1ZggdfFLqSbsbrInCYYr6lJLFHdEXCKue/eoSIoJeS8g\nvTGQVMHTDa+As8tWGwcyUSIUiWyMheM01WuCCGVe4Y1QELHi/yXRRXMQYfnB\n74k6BFfHI/TEFHY1TDRv8e26F343mA8j+uvLQc0f17usbGR2vu1IwKiAfduW\nkn+DjVeQXL5JmjTMw1Lw6iN4AU185rdF8rthEMT4qzVCJNFe67Xaz628FmYo\nGP3iBtnccfEpbxyHAa2UnG5Qu1j4cO7KAk59eV67Bx/tBn9uSrD3O7N+o5OH\nESUQ\r\n=HdYP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCJPND4oLIgsRL+Z3AZsvDSikouAmoPD1jAia3PIi0cigIgZFtCZldqoligEQsY7UxWxvZglzD2Mqu7sbeLgv7+rSs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.0.0_1606129321812_0.2006091536485206"},"_hasShrinkwrap":false},"2.0.1-canary.56.77d9ddf0032ef997caf6b9132fe21d359ded30aa.0":{"name":"@sberdevices/assistant-client","version":"2.0.1-canary.56.77d9ddf0032ef997caf6b9132fe21d359ded30aa.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"77d9ddf0032ef997caf6b9132fe21d359ded30aa","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.0.1-canary.56.77d9ddf0032ef997caf6b9132fe21d359ded30aa.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-yYiDUxIxuUEgZx9nbzgaPAEOXXU8VqSCgb+Ubs+UsjCmTCkhnfYxRPd5Ht6TsSQs3na/mKPjz3NUphUIP2bAUg==","shasum":"bd096eef3fc8e2af45c24c9ede7433ae73118c1a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.0.1-canary.56.77d9ddf0032ef997caf6b9132fe21d359ded30aa.0.tgz","fileCount":61,"unpackedSize":641513,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfvN+TCRA9TVsSAnZWagAAMcAP/11CjPOYvL/isx4/i+2N\nPSE/ruhsY2y/79fDA3warVeKu8LehF4IErOfD0UcwxKq3hIt8CfSJtGxx0QY\n0H7LZ9X5aCoW9rx0FzaDUcF9x9njuq4P6opauV1C/JsjcYoVxgLEQocnYQzw\ne1cHRk4tHTxsw3CtHVQ3qvAIccukhGfZ8kfn8eunVp8rtDGKLrUSzaMDKJow\nxvc3Zcs8vn1ComM8x+Ed3QcyDVyo2SB7um47WA5SyMXl7e9WED5xfcLyvP54\nvZ2S9Y6JZwfqQevOYPg056JV874ihRap/Cnt3v6HR3Ps3BzoOndm9jYUUuff\n6Bt0snEFQy3LND5WovMYCUE0/krRFXLCb2I/GKmzNccaHt8Olljqp6RDKKqa\nsCtMUwhbvGOyu7pfDslZzNBrwRDwVDI59BmlPnDuZfH54NiFmgb4aIH5nIry\nv4IKYJrfNd8G8simifSnKZ6OcmcJK+q28nFczA/lG2ae9saIx5N+D264fa6F\n55B1k6/t5hQTLTVhRXlcD0PTcy7nCzTFnYf6R10vLGyfn7jPrihz+SzaUmWz\nu1QDOkUWjc0fA/YUjS0v64UjCancxlHZOekhTxGVTe8ZKQ82utPk1P64Tomo\nClO+GM/z3IEeRC3oycGryMLbp3QWhHy9UdtvDVh2la+mjdqIRrdBz98fJsmx\numWU\r\n=lsce\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDoLmBVHV2TuNjK+MqcSU7cl9mD4wpsTdCyDIR+0wx5XAIhAPzDqTo9TgvZpST4RGYNrxceu5NrChKgtX1BpaNVNkZ4"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.0.1-canary.56.77d9ddf0032ef997caf6b9132fe21d359ded30aa.0_1606213522602_0.5754931890264239"},"_hasShrinkwrap":false},"2.0.1-canary.56.9d7ad87acf790f295d1353db76998f74538a020c.0":{"name":"@sberdevices/assistant-client","version":"2.0.1-canary.56.9d7ad87acf790f295d1353db76998f74538a020c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9d7ad87acf790f295d1353db76998f74538a020c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.0.1-canary.56.9d7ad87acf790f295d1353db76998f74538a020c.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-2rRq17N3PzkuY8oXluYIAD5K7TBJ2a083AG4kBOtJzzPiQhAeDFR0zt9rc8d6p6ywlxgqTCrOVCLNjUS1+mveQ==","shasum":"7644019b4c5765a3e379ebdfcaafecc7c1a838af","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.0.1-canary.56.9d7ad87acf790f295d1353db76998f74538a020c.0.tgz","fileCount":61,"unpackedSize":641484,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfvOLtCRA9TVsSAnZWagAA6RYQAIDd4lvgOXiSb6uuemMd\nqXk2alMo1Uzz6w2wHmSDaRkCk828uOLCbVc+NbhSt4J2lIbd9P8/nmZg7hqn\nkud79nSxaNTnEurmyeu9LM2uswZn2Ltv2MmSxJSzCYdhU7S1S4TqMPGMaLzU\n2jcdfklOZkkHC/x2Gsxu6sVhvWur73WiRQ1r4YOF6NVaET8V0lzur7Qko+qc\noMsvv4YOyS7OA+QY60+xAgVr1N3+vTYSrxcDBdv6lO8b7A6HsADi2yIIyvGh\nwg/VxE/EMwdbwXVkpxnraRWxLJScxa+4ry+WkhCs3rnk4pxecHX8lX8r5nul\nNQtGJ2SqclnXPY26Jox2bvr5YgaUJhfrE3LVwmX8fv+W2mSM5lStV6JI9S0d\nZs/7m3lhk/+Id2fJjCrL2vg1fBGN0QXYHBFyyXBH1JszglcT6LiUAn9I9vK/\no4f6dQ620hzuSrPzstFZ/nm1pdONBvR7DGvyuBv5feZijuhsldVfqFICENB4\nibz9PGbqUh+BnrtK+llSDOSzye73NVfhpL8J9lly7xLeBsbl/e4eHj72F1Mi\nuYY+t7lgC6ZhjVQ8GVVbBcE7CB3SkXIrZ6/5VRQXmYsc3PYVNFFFPlhNvOSH\neqSiM8j0XjRt9eWdC4wT3ceQ/3GMlltZLsnzVdP7U4L/8FWaNjQtnOLW6xZN\n8AcG\r\n=yORf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEXwXthqAtTfW7azkH6uzEVwANOBnn/Y8LbiBBClx4axAiBpL5QtTB18tjQU92M5B7NtX3qxd0p49B6UHSbRIyfCzw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.0.1-canary.56.9d7ad87acf790f295d1353db76998f74538a020c.0_1606214381485_0.27159340938444765"},"_hasShrinkwrap":false},"2.0.1":{"name":"@sberdevices/assistant-client","version":"2.0.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"71d27fec957c03843cff63a26a7543fc1057258f","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.0.1","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-D8yd+2XuhOa3A0GGN6Cf5wQLCSRp78tez89iOIRqm3UhFZkPkgljMFEDjEdH/umaugWuj7CZHcELHAeJzFSgAg==","shasum":"36d66cac277c95f52f3a2e0c87f5516cfa0c870c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.0.1.tgz","fileCount":61,"unpackedSize":641761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfvOOhCRA9TVsSAnZWagAADHEQAIIbtGXdy9+VoFiyExX5\n7dzZecwiE/DrBL0hMJjF0sbk9ZlLrKyvGKd8509tWaZpEWztbvCFDF2B3ohf\nxnrKWwM8DUpo7JSxG6ssl6oVYHhith1eLkWQeKSxOGo4+CBEIqYvd96CLoAU\nC2okRTESVPKymzU5BCd7fectlc06m5MyliL4Os7wicmds2O+FYgrxpFhmfWb\nMBI2sitwxmWTpce183WLzclDuJzT4qdTOyHLYr/D1y3mRR+/9TXguF/tfBH9\nt2bOUYDLcaHYlPqyD2uqJLjwn6NhPdCNbcGvDFINwdKpGMlR9WzlQ+aHMnO+\nr/VjwvUk9cJ6j1NgcsONSrowF/fXoA/m2vefl5tRQe4xhxGoIsy6k4I9tTw7\nE8rmieUtv47kpdynNvpBF7bFvwK121r1pWAXH3hfevnDUijve+UE2uA9Dh+Q\nK9b2ZV/eU2matjgNE/Ib7R6PIzXKDYE2qJt+czHfQfQ6CuRfoQkoEzD/IVXe\nk0zHjo8rdhFFvmIgImGUoZEsmVHwWZU8aS5M6LyiODvFmiAk1qz6mVjegKjJ\nIM9zwGxPZVCR+IovdhkUo7XJaEkxaxMYBbuBlYs0xdy6q9xQZjV/JHcBx4U9\nzC52riaxFUZ1yPxMalYtYhxQqeJoOa830Z88GKhIoBUr+K1wk1zpVR3nrZoh\nu/zC\r\n=Hz71\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHhhxm7/+ufmh2d8suVCzEbPw+T1sDAWOX+L7DT5KAWLAiAkcstUwL0gsXHWn8Om+kKZ3UrS1mCo5gFwtM/rvTXfJg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.0.1_1606214560641_0.9840716110869117"},"_hasShrinkwrap":false},"2.1.0-canary.54.6ec2b028f032e82d874ccbe772a73622337614cd.0":{"name":"@sberdevices/assistant-client","version":"2.1.0-canary.54.6ec2b028f032e82d874ccbe772a73622337614cd.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6ec2b028f032e82d874ccbe772a73622337614cd","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.1.0-canary.54.6ec2b028f032e82d874ccbe772a73622337614cd.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-bOTOooyJxknVyrKZ1CQOL/Fkuea6wvo+kSCBRtbTSTxoVBbe5R6/fwxlkLgpX6rRKjNgft1wxIM5wIptT8R8BA==","shasum":"c071ffc857238e828c694c54b6e7b0be7f54a9e8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.1.0-canary.54.6ec2b028f032e82d874ccbe772a73622337614cd.0.tgz","fileCount":67,"unpackedSize":654453,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfvPOKCRA9TVsSAnZWagAAKTUP/1eLOTDI830n8wX0WFmP\nEnzliyFTdzrZremVM3MVteFxCsSZW3bUsiFRK6J5+BwC5w9raYP6nQWg6lLy\nmEpxrzrDS5Y09mMr89/kPGYyLDxP5dALWW527qYkWYv+Jd1tiF+eLFoxzqLQ\nQDRLAhdu+CPy4c3pwY1XjJIBsFGZTN0VAtuEPM0ap6ousnHa3Xt8tKPFx+xg\n6nw9RiGRouogQ0/p5IYvzQcd0T7dxIcDIYB579IV7jPpWsgdL0XAe6RsrTGw\nog7OFUIBgVgVYEXh7BCbKMsTyBaNWcNuSdmBfZr5Uj3I+ewkchYN8NK3aPHv\nkC8GVO669WUwle+eizvoPvb6RbYC949yyk86sg/qTOoWrh6yo5myJ1E1fwf0\n/LUx8xv1a6uFkRwmyFYvlCCkAWzy8qqomZHqcNYVK4lpyaye0KtsFVRPXZqo\nA3GPQWAHE5b9EFX1SPUF6gUC9TVPXg+yCdpFW8lapQB6huwCB8qG+aqRv/AC\noLumaONK/vzOnVQhcQNhDM/LCWMucj0SjiyYd8hQ98js0MjaEs5NEAOyPSHE\njNmAVtv7K/2d4gpSYmgLeTFn+UR6yLKVy8eLkF9wbTr455PAPdODvTEEiIHx\n+9XsMcdU3h7jAFq0zs/lABB4g+/Ml/PM0nDx6NrlpifBojuZ2DZoPtMd92t1\n+hR5\r\n=/ef2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDNbQ7crJcAoIRnJ9Ei7mTmx9whhUFFtf2Isk16agoaLgIgCtsI723ttLSk66Pb77JOlHj3p+/ASxEUGwx2VCcz5YI="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.1.0-canary.54.6ec2b028f032e82d874ccbe772a73622337614cd.0_1606218633492_0.4916528303168868"},"_hasShrinkwrap":false},"2.1.0-canary.48.ea0c2f160930cb103ea97ab3254ac0298a60de50.0":{"name":"@sberdevices/assistant-client","version":"2.1.0-canary.48.ea0c2f160930cb103ea97ab3254ac0298a60de50.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"lib/index.js","unpkg":"dist/assistant.min.js","jsnext:main":"dist/assistant.esm.js","scripts":{"build":"rm -rf lib tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir lib","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ea0c2f160930cb103ea97ab3254ac0298a60de50","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.1.0-canary.48.ea0c2f160930cb103ea97ab3254ac0298a60de50.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-PFvDDzQpAIgiFmRARg84IfwEVZiWnbhXofLwhxtgDaZE7g7nIFuP2Qd1AoEFTncnmxqgilOnA27rZu4vf1bitQ==","shasum":"e60f8ef09609265412d83ea5174616b2bda2aeb1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.1.0-canary.48.ea0c2f160930cb103ea97ab3254ac0298a60de50.0.tgz","fileCount":60,"unpackedSize":1527739,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfvQk3CRA9TVsSAnZWagAAke4QAKFI/ZDr0OBYlyxSa19U\n0U9Mt0fgzP1TVmFplaxeD9jcT+kpyklfKhMjI54lfAihR1MTIqpvHwnXkfUU\nLBSNEFd6gLLc8d77THXHNyRWRYZjY26HcfhIQ2PboiLnF4vO82F1+dkpzJI5\niVLeqCf6CoTr2D8a/2YRS1gH/G8qVtu09U7+UbR/aDw3F7aSAYBi+71IwKpc\nyAHcP9xtDZHsf6QA3ytEhFvvP+ZE1mYOpMgDa7N1QWDOnfHG9huWlAy7e3eJ\nOzWUhq8kJHRD8dNPWqpZtF/lbQjXPFtoHTfGXjCIV7LG2eDkUF0b+HWhJ722\n3t8q7KUTbv75MLXsbTdfD9mGefjuJ7HTcsZcbZoK8qwhMjGHCfMb8l3cx7gT\ngZ6Ei9I2WCAzZntQiR+J+/fzZ1EKg6kWJLzaLS0Gcgo45ooYmNX68IBawmQr\n3Y7rpJnQqhU2bXNIEAqxcK72oABpKQMMnzMo1eDvy1Obpt2nyLo7LKQFN105\nUToM1bVJXHopYqel2/Qo86hbMO3hKYb1Xv+ZVqHIHpRhVxY71VQXYUCvyK6z\nXpqesH8y/NcPmuHRa8g3j6kQfk3MCEQc1I6nwBUivUyKoquOLRACu/glgrvS\ntfWBtmNMEPXJsbH0TMpDLnfjFMqWXGAdiUSkNQE2tXk0/+6WjM+YQcRyCTcu\njuNE\r\n=Wr0h\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEdkjFUn30qjnp5Tj1aweZ2fYjMa8E9qOw/++Wu0nTN7AiAjA02Wlod+WqkHJYzqb31qgONhug+a+HiJB1RUWmFkdg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.1.0-canary.48.ea0c2f160930cb103ea97ab3254ac0298a60de50.0_1606224182333_0.8889276729232347"},"_hasShrinkwrap":false},"2.1.0-canary.48.8b9ec1c1c05e3931db0d09310bb38b0595963367.0":{"name":"@sberdevices/assistant-client","version":"2.1.0-canary.48.8b9ec1c1c05e3931db0d09310bb38b0595963367.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"lib/index.js","unpkg":"dist/assistant.min.js","jsnext:main":"dist/assistant.esm.js","scripts":{"build":"rm -rf lib tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir lib","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8b9ec1c1c05e3931db0d09310bb38b0595963367","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/dist/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.1.0-canary.48.8b9ec1c1c05e3931db0d09310bb38b0595963367.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-kWW8TPZ+Jv32kh9+yBQYFVJWrO9bCT8GMA3Iq0VVtZwnb3LRHxB5P4/5DHIqerUtjoOh4KOUrHHofO2DgsUGtQ==","shasum":"9937956921a3241aaef14147948b45ed45539121","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.1.0-canary.48.8b9ec1c1c05e3931db0d09310bb38b0595963367.0.tgz","fileCount":60,"unpackedSize":1528644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfwLCpCRA9TVsSAnZWagAAH7gP/RiNKfXJLNXviKiJMy72\nq0lWggf13xoDwG28TRaesSRT0h3IJlTrruXk0IQVlsJt3CGEfC6lT3vZvWR4\nV4paj8JyVbXmQfvLrq7JEvjnz/CGzzueqbl2pwZZq90Q9SjTNRSaJ1Ef0qd+\nasHJiq4nOW85+ItixIMLfGIpnCwY8LLDL+wKlTcN6plzSV6QAGVd5XTXeZGR\nGbJIlJBdsetNOe0AG96uJcuFTpjfTOnbNJewK3Avm/uQ0SzSY7UV+p1RNDeK\nCNA95lGCmCgDLfjYtgDw5tsQ1q05zUsUx1pv6NWDGiy21qBJpnu2cjgfKIT/\n2kT5Xrv4IcXKDm1n5Xvv2NsMcGkwQ4byaAvfnX5q5ZEobrnJxPGgmpVulcNm\nCUr4E+UflRVC5ipg9Q3YKQstUXhpwET5kQJp2kC7wMpBzmQJQQ18vvtT8AP0\noG7qfnDyCueNi8vPD9VjYAwpR0dwMCWkSqB16VhoT5+QkNd0/s8IlDhY/PxJ\nompPgaX80XwdHHHbst1BLkpTPpon+hXAHY2rMROnzCCFWKSl24JCBcI7rM3l\nFEo5qlN8bAMNnkgIOqf7LP8jIn9otJtYOud/7CnG7XqeEhYB7nfpRrREdNCh\nrkVY5xIR4/wcaT5S0lZ/olQmfQgk1NEx0c4WsiUWNpSwbUv0cAhfTMmzsjvQ\nkZAa\r\n=xBOg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGJevuA2MX3zRGUUZSMtQfootw4CdSW22bFhWvDVZiXOAiEAnh6J/v1tdomyEe3qiWOxwGvFKrfKe+uAIw4VysYzzyc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.1.0-canary.48.8b9ec1c1c05e3931db0d09310bb38b0595963367.0_1606463656852_0.7039123404727687"},"_hasShrinkwrap":false},"2.1.0-canary.48.c9b4bd896244a171c6aa949c04c11565d8486fa2.0":{"name":"@sberdevices/assistant-client","version":"2.1.0-canary.48.c9b4bd896244a171c6aa949c04c11565d8486fa2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c9b4bd896244a171c6aa949c04c11565d8486fa2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.1.0-canary.48.c9b4bd896244a171c6aa949c04c11565d8486fa2.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-jAzIsWytmKsGSxdbJGUIz6YAqLQyq9d3pGB/lJd41w8cobQNI+lVDf+iRPZEPQ/3je8QAwk+B/PunFSZ/K0etQ==","shasum":"ae2404c72ba1855c705138623af2c7c8d6f76bcb","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.1.0-canary.48.c9b4bd896244a171c6aa949c04c11565d8486fa2.0.tgz","fileCount":58,"unpackedSize":664883,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfwOzOCRA9TVsSAnZWagAAy5sQAJqUjBrTMZPrvrTsMXDg\nhe9MpB855FH4Sjro3lQbIu9shZdvDLUHOGEzMQ5ttH+h6gdb6yRMPH6DON07\neUFmjHO1HmOfLZxqrWLMbgurqM6nfU32empB2ezozaTXyqdyQ4cT35Vx90rP\n49WMxKUvFedZZq/0PZUx4UvvwVjvdkVJwP0zIYPMgnE65X7ii6ZwnfYO+gAK\ndtjHfMjkDHjOwzbuIckx/CmNJhPT1DksJyM1qSBy6mlqiaDI7hJlvpKDkwug\nFLWsjBxbRcVPN/F2DLsofZwfGN5UoDCN58AbvagNU6nv+uyLMMqZ4kkLTyXV\nrs8uknN4pQcW2N2dz3H5j+JTorr341XsfXGrO3hlANN8meS0EIlUk+Jq67EU\n9Tf/f3VTg2mTg9m+vB0TyoNxS9bMK0v5YZnmUXjCb39YaWcFDrxoKxivePy9\n/kYUKe8HOonxP8eiEsAspmg9N+xUIUfcdegKHhADFyNoXpsP2Mb0UiMZHFwC\naAX0PkAWVW1lGNq68+69DX40dTxsz/TEkHr4hmQqSBakjifhpRFk+7e9OZ1Z\n35HNpxVenuY0oWXs5saHgwSkKGvmyysqFHOtoPoSUisrcsZclMy9EcZHPH7A\n+c/ruercByD+v2Qgttfxr6cCp01bfkiU4Qo3vP0bRaa6jug4wksbjCQjoP7v\nnAd1\r\n=jWkp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDVpy8NTkvs4gBXvZXhiLO+oRhXuO5t2NtBLRqQ5F2ezQIhAN1XDorexuXt3YJr6EdEZTplidr5t2tJK/cQQyxgHEPm"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.1.0-canary.48.c9b4bd896244a171c6aa949c04c11565d8486fa2.0_1606479053663_0.08454010187026939"},"_hasShrinkwrap":false},"2.1.0-canary.48.6b2c2bde242df6b5fc61c16f3265c389e9b5c041.0":{"name":"@sberdevices/assistant-client","version":"2.1.0-canary.48.6b2c2bde242df6b5fc61c16f3265c389e9b5c041.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6b2c2bde242df6b5fc61c16f3265c389e9b5c041","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.1.0-canary.48.6b2c2bde242df6b5fc61c16f3265c389e9b5c041.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-nEEgCJrfSQkSVoqWui8ZSuzAvE60PQf9+nKoSLWu7mSULrhmDk/cC8eA4ieMeEyJaJW0Tx9BojHbiu2hHZnnyA==","shasum":"ccb6ef12900bc7b8adb3b93777c7b22f6734f389","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.1.0-canary.48.6b2c2bde242df6b5fc61c16f3265c389e9b5c041.0.tgz","fileCount":60,"unpackedSize":1528418,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfwO0dCRA9TVsSAnZWagAAEEMP/1pk2qC27zyh1I+gUGWZ\nDTOx4Y5apahPCZZy4WHQ5Z4EV/jMfVTUv/6Nx8dvqaRAk9gJNLKZW7Pyy31X\n1e/r7SXxZBeaRmpG9184w90531UKTtDzr+C9KEk9q7IoHBKko87KoNoT9Pxs\n2kE+FFiRZWZWbDID6jin6SJ2/Hp9UxLJ3A9/JBijVChr+i2b4AFhRo+VurD3\nqrBiQGWn+dLBg0IMb6gHzVEH3QoVWvggj082ctsc7X8SjE8ygvc3wbV5sxRz\nkZNGvEtYo4UXjHvarvSAZU3lZoPchyfTU3tthxLGmnDTsfoqVL9C0EKuyQHL\n8C+RH1m/3VZr7FEIDizMS30K/zD5oZknMsE4QAuQXn6sFVJa9ZmczdM6SEff\n40S2SuzTXliLxa8XB5SHljF3wrucAG8WXg+toBrtHAF8NZyJlpw6RjFT0DuA\njPvGbtiIreTmbM4X5j1t+2js9FOeKR2SIj4NpxLm29Hw/ZRpmxHI5IZUOoU6\nbVTAgjW2lUuPa5QmZDSBMp6b69/3lRSCn5uSTheE0L8b430MWE4eCkDg9dID\nUAJzziCruaBNhT+ou/A9z/7LYdKm/TYFa4Wv5VsC/DOKdTglSe2DLhJ3eoHD\no34GGFz4AxbxP2aP/FSbpbkvDpZMeGmbyUUFfbB4EJ/b23ury+FE4Nme7R0n\nzKkH\r\n=RuUN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC8ffz4Hz8J+QOIrjzKBY9N9roYkszqxFt+1kfqdh329QIgXgx854ZUgnTENDvR0ZQ0fTih0FSSok6lsq4ZBcOUkN8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.1.0-canary.48.6b2c2bde242df6b5fc61c16f3265c389e9b5c041.0_1606479133073_0.05505151175744749"},"_hasShrinkwrap":false},"2.1.0-canary.58.03ac302471e41b44fd8a93a6dac19b85a3693042.0":{"name":"@sberdevices/assistant-client","version":"2.1.0-canary.58.03ac302471e41b44fd8a93a6dac19b85a3693042.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"release":"auto shipit","prepublishOnly":"rm -rf dist tsconfig.tsbuildinfo && tsc && rsync -rlptgoD --include=\"*/\" --include '*.png' --include '*.css' --exclude '*' src/ dist/","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","ts-loader":"8.0.4","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"03ac302471e41b44fd8a93a6dac19b85a3693042","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.1.0-canary.58.03ac302471e41b44fd8a93a6dac19b85a3693042.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-8r+PuFMuVziQTF1RyaXHusFoqppLxq/AiQYefhZ6LNYdSmYIjCUr17jdYUMgu9ohAG3c7TI2nEqnR3M2IS0KCQ==","shasum":"f563bfb512dc855e15e2aa86b86a0ed4c8389f4e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.1.0-canary.58.03ac302471e41b44fd8a93a6dac19b85a3693042.0.tgz","fileCount":61,"unpackedSize":642553,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfxMOECRA9TVsSAnZWagAA/ewP/0Oh8XFr/4RQNJl+hBZh\nMgHycqMk6MSxgqhmJYdd2Fq2pTxa2UeixHOPz6Hr/IhhKfw2Skp6gLpUCV3O\nWZ5Hz2Y0oul0CHm5Dj5eiwMgzSh7sah00aQyASinoYYUga6CS8f0wJiSeSky\nlhmuvO+n6mnmXpP6wkcrvtPCtPMpxibgybkXuG1MEkwAQ477tD66/ibFTZo7\niOvY9+tFaBxF/u+aW5ovMlS1iuoA5nc/HVEX8deKd/xghwzUdp+tOsU7b/vK\nCL837FM4YpEG/vUfdIUubkpVm4hKqmK83JpTht3FUQugae3oRh83HfSpFzfs\n2OQu+0DMdIG/x8OH6MaclFyHQNSkx9jP72I+gsyXdauQ7bBK5R5J36ioxr+F\n3/6yygHZ8esV7JnZFf82XCYLR8RnoIf0m6gOozZ5KJMVcX+XLwZg2AjwPb/k\nc7YO3kJ11VHA9YlDCb/+APQ9xt4pcT9sGbLBdmpLOfR3pH4Usp6KIxdrR4Tb\nooWQtHHoI110VbawuAZJvqvA65Mh8qPWdVzvwadmz89U0wXmUFXwqBwvzotG\n1K6K192tqxIJVSQES+wzROjE1sL6uymEBKZZL7mg6LlDi+APncIYL0zarDVv\nbsf7nCnhcwzIAx1vwY1x0o/axAzE7kE4wtp+Y8KkEGAeoHQ2DeUbboiPZK+j\neAdj\r\n=ODKP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDSDK27/merinbYKQVrbnX18b5svMf+Pn1W6ciWXbvlFgIgD0ZJ/lPM8HQFJbVBGpI8kPbTesJRH2fAa3LqYsGzKwQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.1.0-canary.58.03ac302471e41b44fd8a93a6dac19b85a3693042.0_1606730627331_0.38568864099221223"},"_hasShrinkwrap":false},"2.1.0":{"name":"@sberdevices/assistant-client","version":"2.1.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7b148369270a0a204e38e66b0e1e09b34317c494","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.1.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-gbuediGbgYXSK+A/c33lw1nQWUUFGVHghWSlcmxaS4yskSsdRLnbpo66hIek6QkU3ecP7Zl85vZIp3OKI2dCYQ==","shasum":"9e7ff347710229759ae3bfb02d402295d7b0476a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.1.0.tgz","fileCount":60,"unpackedSize":1528790,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfxMfACRA9TVsSAnZWagAAEaoP/2d8uK5dXt064czMHDkQ\nob1nGGoFUbMjWvBR11OHKowsnlPUI1PrGTMoXMEtp2pZ0fR2S9lsga2e88oz\nGpsFMOpm69FYdb6xurTOhldYHc01cPVE+MPTHhDGUARwP6Xu+QP9fzHmZ+3F\nkgRqddRaY/oi+y7A5C6aH6WoyhBmTPNMBMTUPda0uwJLl1EEv/dToGt05dOc\nYUJji3YOwL2pSRN4nqVI6tk6nF9c35157gltB1Kx0pq+7i83LT2iU6VNQOgm\n7C98lRX1grR9Ok6sLVXhA2oqpWA7jwD7oJ2oDsGn7exIM5PCHbTPBc/3s9CT\nGKpyVyPSbecbm5rlLE25n0zsJeeYPkSw/37nGT7h54/RaXVRYd+7UWflEezE\nTG7ZlJMgMvc0PUwV7THaN6kBUt3j8nDNfxiUk1CW5UdNLgXZcJLM0qRsCyIQ\nu4pp6IIqLWGNgCjj39RbveY9gHWjHQ7XyTekW4tvItdZdnyeh1bF4DGvLO2P\np/Sn42Dift/MKh2GZxDaXVA6mAQWS/8M4mOSvccqkq3zMduLdKv+Sh/erQZ9\nN8yQStq0bLuCVN+SC/iwRN4ERtj+SILXO3Nn1xDbFyKJzmuZ45GyGhdw0p5D\nx6aNJnX/Glh7iK5+VmDXNDxUbLQZCnejRf9CtA4nRhUx8aMdbhL/QZfYwcbD\nCka6\r\n=fxED\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEXv7QdSZJGJLwpxbc8ZN3Ie8VN59ZMJU6cp2/SeeCxdAiBfwuSqg8EQqHlM1Vt3S/kuA40g5hEHpj/bzys/3Jmglw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.1.0_1606731711801_0.5512223390059532"},"_hasShrinkwrap":false},"2.2.0-canary.58.f73e9ac794e22bf14a958314a79b9a47704e1438.0":{"name":"@sberdevices/assistant-client","version":"2.2.0-canary.58.f73e9ac794e22bf14a958314a79b9a47704e1438.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f73e9ac794e22bf14a958314a79b9a47704e1438","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.2.0-canary.58.f73e9ac794e22bf14a958314a79b9a47704e1438.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-JqzQ19wdpKGaG68y1YmOTsB+AYSosebzsJ3gq6CgbJl1zkHC7RHIKEB8eFId12+EwK7BrYq6j8KRf8j5LqCfmQ==","shasum":"06ad44f468d72f938477f29cc09d7c5e2302e18d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.2.0-canary.58.f73e9ac794e22bf14a958314a79b9a47704e1438.0.tgz","fileCount":60,"unpackedSize":1529348,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfxNGjCRA9TVsSAnZWagAAc6gP/0Ff8uYSQPMyAfkc05OE\nqcy4QKEXjfz6wFcDIClTlE52XMTBU4/iMSX2fMvC6fvCmw1LhhLvyoTQqnYG\nnRnoVE4+jQU9xMhNXIrCrw8g5GvwFMYibE3roA2XqFcpVd0KXCxhLcDhB7qC\nQ0oIog0AUqXvHwdCF070v5iHGQstqa/THQaxiRGa1IWOX7k0LM1o+300TE6k\n+4WnoZDYyHaXQFTUNb6n5mzfmMaYjiyRA7ciFXXsOWkqPL3dH7B9vSl4dFGK\n408//UqvyldkK3FzcI0/7ouUtVrgWb7dY8dI/Wmg92q9VKsi4d4JMDvGQIa1\nyhrSFnzVbJpRMNzz1JgleE2AyV56Lh3IJCalVkJtHJpud7D5Qxa4uix5LBqC\nj8CoYkCRxbT+2+3dyOJVVri1p1wBEGDP47zA3QvY711MKIYwF9QukvLMNXc/\nvG9UBtmEDeh5CtT42GhJ34kWqbhm5Dlmnem+Zql4bSreAAW51oDOMJ0uvNly\nO2XsHvEiOUMzPmiddpLTa58UplGuJeepCsf77NEjKGYi8/9mffLrQ1nWD31R\nPVAApywwyidlFDdYEEIxPUj2tkJOcianYhcyLDVWuR/yPZC+7TFH9X2Xlo+B\ndbEDVAC0gNuBio0xJ5TiSAZPtwSXqa6UXNo2OtxZQkEjmAF6pJTeZLTrFt83\n5UKB\r\n=aSSe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCfe1Pld80sXyP9Y2j3L9/T2fpaN5DtAYn8RCLwNHlWDQIgI2g9KFtyMgZeRf8Ij/aLdvzUSLG/HKdjB56ZJdfp+ac="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.2.0-canary.58.f73e9ac794e22bf14a958314a79b9a47704e1438.0_1606734243120_0.340660572650787"},"_hasShrinkwrap":false},"2.2.0":{"name":"@sberdevices/assistant-client","version":"2.2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ac3a809e62253ac0eaffe079044fce56cf6cc88b","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.2.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-dOQxRbfB2VTFm79AyGhYUZJ3wjIMC/sehjbToSAB/MUjJJhewN3YMdcA/+Oe9Xfm0hpi9avNctMTwf8VPG4aWw==","shasum":"86de81c65eec54088ec9b5e3c851fbd0f8193498","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.2.0.tgz","fileCount":60,"unpackedSize":1529632,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfxNJUCRA9TVsSAnZWagAAk04QAJvZKvOWJgdKqxBEtEyH\np5mUuevssl3SRdlJ4Lm5nW4IC6LLEjxdvCbR5B5/xkX9FTa+UpcBw9PTsTtK\n77LT7d7JwtQv9Wg81psLXvKb6nmxy1hdDnAopbhXGuaRWhUyPJmjMTdByCp8\nXGrb5ZOZR1HxDjfQE/Zz3w1idZb1uXHMHjUgh82Qvur1V80yyz6bQ2JK/i4+\n5Pd8/1NTHHAk31jfhx7528z9DzkgYpLStRzuOJb/ehpm5Sn+NFvhg5cNrSHS\n0zecq6NTKBdqoHFpE00cGc3IM8Zb4Qrz2YHz44EcT9QnvLlEaB5nn5igN3qg\nG4cgVuHKuaWg/E84OGb20F1KVH0U126sgfjqaBHBp03w8Uo0+zjaVM7UOGYT\nUFQI68r2+UaRxTg64TmSghV04pK2ouIFE6Qxe8IK2uF58Q3iV/Hwi4FYtyUY\n/4Rs7lEsyPINVFFcxu+TrGp5zequcJhk3DVPR1DPswh7eadJnoQTdIljJoCu\nF8hS/owMcXZDUQ0tOixlw4sjjnLGpqqOoDn17TQtak2JqFYf29vG6uQGJZMQ\nXyYWJbjK/sMCcT3mYdFFvvluYo5Qola+tQIK8BfWfEjB/DQQl0yAy6a5UImB\n4DwYcrc1UDxFd9UYR30uvobarnxn6sXrjViGbs0D5rAJpWBMAA3PMwTGSG62\ndBug\r\n=1b9Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCEonuyD4sZuNc+QouBFfA4pKAh9W31RgfgPHsWszeABwIgEPXT0/dJCoku/Uv+YQvALV688tc6GmTD5ofEpLKgPJg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.2.0_1606734419903_0.38780807493070135"},"_hasShrinkwrap":false},"2.2.1-canary.59.5e777c992b8bf1d16c3ebee65339b06f53dd4fd1.0":{"name":"@sberdevices/assistant-client","version":"2.2.1-canary.59.5e777c992b8bf1d16c3ebee65339b06f53dd4fd1.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5e777c992b8bf1d16c3ebee65339b06f53dd4fd1","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.2.1-canary.59.5e777c992b8bf1d16c3ebee65339b06f53dd4fd1.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-vz/3SqJ0uI6jiELUe6glRMwhozy0PQAwxVlRJSyyiwjNIOgJN7HS5xsw7H5Bv52IrMm9tvMbcg3WWO0sdT0Oeg==","shasum":"1068a9c72463de4fe69a4586ff0f7a9f0cc03e59","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.2.1-canary.59.5e777c992b8bf1d16c3ebee65339b06f53dd4fd1.0.tgz","fileCount":60,"unpackedSize":1529877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfx1lECRA9TVsSAnZWagAAnVcP/2VuEFvYfpyrDTXk9Wpn\n8wMB5hKOU8GZu7O/laMpyN1XibyFD0qVDgXesloGhoQdoL+9JuttueN88aiG\noOqDZ8Skc6ZCpjbGfs9EomPeCAUJrxq/oxirrFX8+/IpazhYDeTSuyXSijTa\nB18gAUynhx/iVvj09ErC8R20JyHNXAOhAwnlpnfKWDxnp7ict822I5XGIyf9\nO2CbSx2kdToJRf9wmlCaqrIsXc6RpATQU3UlqSUCK/4JRdy+r2FmnJLyY3Pj\nhqakWRT7ZIrvOhZxdJpfAa2HruTTFSVM4/b49hJwfEE2O48rpIf7sDPbHdfV\n0WKOgI/875+K9c7MQFRG62jpLQGJGYKOeffsRyefoOChOmndgvabfnpEbfff\nJ4Yh1MTVvgbHwmQNA/uBu60OCMcy+h0i8ZSHQzDiQUU5YdSqH2l9UHKTXHUB\nyqKpd/RzZAnL/j2vshBrbYMNkri+h2CYfRUJsc3dnKlsfKVHYG+4RUAJCTc8\n6ExP+x9006H2xCp6AwlEKGiCB2TrVwW2YQuNrmDEbgFYzG7PE3FT4IiBGtuE\nk6zmuwXxCdfzA8strP9jsbAq9/9Eig0CQRtSBoY9PWTAtP4fdayZ3zqWIOYK\n9sH8oVDreFZyA7a0SVstScgiThnbE8E08hctFU5Av1lyXcr3tTieKvsm4N7I\nNT5s\r\n=akOp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHusdcYr8T2P5INBgfI8wjbaagiKZH0qql2rwMrhfZBQAiA9IhC/1InOaXGshv8xHBXval6ERQoAFlbys0UDY+ZQTw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.2.1-canary.59.5e777c992b8bf1d16c3ebee65339b06f53dd4fd1.0_1606900035385_0.7005721581847932"},"_hasShrinkwrap":false},"2.2.1-canary.59.512fa8aef00f263236e660625d2208293d6b4372.0":{"name":"@sberdevices/assistant-client","version":"2.2.1-canary.59.512fa8aef00f263236e660625d2208293d6b4372.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"512fa8aef00f263236e660625d2208293d6b4372","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.2.1-canary.59.512fa8aef00f263236e660625d2208293d6b4372.0","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-60T+BHpAG6dnUP1XJd6IfqQwW6iHd3C2na0I7VkBP8yUJQ8xHy1+hs8c+D4qQDJteLHhMRjAg2Dn9h79ef1tRw==","shasum":"d66684c6ceefbef15f254c92e1e6ab61aa369226","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.2.1-canary.59.512fa8aef00f263236e660625d2208293d6b4372.0.tgz","fileCount":60,"unpackedSize":1529877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfx1mMCRA9TVsSAnZWagAAGX8P/i/UtFgS185a+h071Yo4\n+GzFHGtVSxcVyKQMISCvkA5luokhugFOHX68HmRLnf3Nqto+VWgwARyH2tg+\noSkbdNCUlDH/sqILkXMjQg/bjslsGcMHsJEZ82tV4E/13RwE+WylJ5IS24jD\ncR88ZCepOeu/H9a/355Ct9oiznX210deOzRmp2RL9GT3H4s+V9t4wwdGw/qC\nxsB0B85aX1eKnbImqr2beTakl0i9LobZb4fBlhbYd+W++9cRCGC05anA+dgu\ng62L3iSGlUFkdqRSqGqwB23q5nf6mqElB5LmR4EblWB81KbeNwVzbwSGj4o4\nW/gbQ3PQJMXlBCg5UQoVRsMyJwFvMg7teeYswyu70hIpQ6DsMRyj+6aRtv2R\nTGcA7aaZn/KU/cioOSs6SI0GlEs55JFWpobjntf3dZorB1z85/cuQg+n3qGm\nTIaK7i2HtmKGXUzBQ704icPo/3Z7XT+s8/hTMPVVIDPW99+OvB51GQXi9vZx\nk6/g9aVPGa44ZT0COj2R0oWaEB5maK9qWgs4rZ+JXSIgYRh7n1kp31rUSco+\nGXX/NEvSi8hgq3ntKf7j2CCUlqhzbVQEKEIoP9Agf8X4AZYL255agKeNubqg\nD46hcm2B5xtPJoWf+BmSgVa9byshRmd0tKNGtuuo3tIuzgsaA+rimFGZ+Bf9\n/lr5\r\n=LPhT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDS+Big15NkNltavXt5U3hoJvP3YI+p6skoBPtSNRDRNgIgKjXwWL3kTk6+o5w7tkCLXewXzuYroaNU6tRyXXJ7MXo="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.2.1-canary.59.512fa8aef00f263236e660625d2208293d6b4372.0_1606900107459_0.1872049457863696"},"_hasShrinkwrap":false},"2.2.1":{"name":"@sberdevices/assistant-client","version":"2.2.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ff46c9582009aee656b46e6f2d0570c57666f22d","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.2.1","_nodeVersion":"12.19.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-BwIBtvTEIDY6GZW+hjQSaJQh6D+SYzsgD2OD+zmYmpP4nIzb5vokdv36zuEhnm9V8rG11V/8ZN7XAyPl7Gsr/Q==","shasum":"382e233334bafde4eb96e128118b239366108733","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.2.1.tgz","fileCount":60,"unpackedSize":1530185,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfx1pBCRA9TVsSAnZWagAAyaAP/R516J+zk5gvc8qlbefj\n3Azn0N6hKFG5kqrgRx62+ViAlLhs++W56uUnMx6G3uo9edjBQraaBeTSTwlS\nMg9kS/t74AjL16IYec6tsMatQ559XXovOGmW6Oro2yrVM2uUX737Irgomx01\nF4Lqkyrk7ToWGrGFky6bqHx/Lgn+FxFAtnS7e6cu44CMbKkC8CHncEqIYv7h\n7neCONy0ypV12DHOukcE02WsxsQGALvthZ409WeWrc5lWYLYsV9/ChB0e2MX\nV1boq/ndj0SU5zf8pNuoXV7wwd5FFwX7FNT1Ds6CtpBe+3yu8CH6HwV/v3gx\nPYNhv+X2Bh+OBn7G/RDsSLBeSuKMdQD30KYKhoS3/CFagfqg2Kg6pYHBlqjR\n7dBIicwkQqjKox35tfD8YU8rqvmtSv3do2ONWOpSNXMDZcQA64CiuJBpmWC5\nqF5q0d5J6tdetQj9xfKO8sGoqTccvmm7QkRtD1RRzfo+nuzpmnet9/LKuhCi\nV74B2V6rErtXk7Gdxh8oL1QT78A/igzzMbaqwEZ6yPgRrkSWRFOuKyOKe5S4\n+yzacvWKuTNctxJ5yU/exmhfktcDTJi/DWEswLI8claaq9nAaka7ZCAfEF8Q\nHBsGwl9R2SXSLAus3HTNIDrmcIGrDpbw4Q1u3jWbv4O0fBSNtK12hgOIZ4os\ni9FL\r\n=9J31\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBPHPFF8OImpJ8AMfqqtS53876Swi8hA25s/IO5pS2IoAiBTlCy1j787fjPBy84VbGNK7HSdYGFrXdL/ceifFfiodw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.2.1_1606900289215_0.3790873059585407"},"_hasShrinkwrap":false},"2.2.2-canary.61.7e255dd5af0963b445c3df1f6de5bd869fd130c9.0":{"name":"@sberdevices/assistant-client","version":"2.2.2-canary.61.7e255dd5af0963b445c3df1f6de5bd869fd130c9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7e255dd5af0963b445c3df1f6de5bd869fd130c9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.2.2-canary.61.7e255dd5af0963b445c3df1f6de5bd869fd130c9.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-fVmYcfaD0QhJTFZnPi6LBcaNzwxhkgOzT4cOdN8xrEZcZo7I2TqjO/eRcflpTxVikoSGhfi5+FbkZQE+7FrclQ==","shasum":"0f9d7f1aed759d03c0090915e15fa585bf1cbd4c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.2.2-canary.61.7e255dd5af0963b445c3df1f6de5bd869fd130c9.0.tgz","fileCount":60,"unpackedSize":1530474,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfyNl+CRA9TVsSAnZWagAAVyYP/1c+qSjIDTwPTKm03dDB\nPYgKWWsaL4exfRpKHLAXyJmFTwyVTWhLILy2cDL+3uGsfYrxw+FuvBbxs01A\nM40hoCr652mLySGkvma3QsjP+C765kFk9QxSaKXRs1VNQUQZosXHdbbJhq91\nG1zQlbHqnm9u49iJ39Bew4I7ev3yTESxpPVdCo6DGtr4U/fRRaOLwCjQVPbY\noeD5DVk2CAMSxwhYrwhU11WR8mjf33umdE6yn3LuM1DLJsfTzTZ8e5L8Cs53\nrPrL3WS0Z7yB9SWyrJSd96CoYgyuEOZl3ZuyDm3xzyTXaXoGM2o0n2fMrVRA\no7wKK1dN/UxOEiDAFEtf4ECO04nwyfEb7fWQcAOMwWYidh7QEBBZURmWWxtB\nBiv24kbNmY4XAKrfcthnqiwHYnsWlIlUVWeWTQ2GqoUV3WxoO+iA6UL7skK8\ns0ug2ORGlwOsoAQarOzHRmHPH9IE2uLnhLN+n5FY+YSsNyObxiLyzavY7qhP\nrOvRvRYaxqiUc/5SbdtBVofVCW5CYIwPbdW5dYynQB4MLcQTptP74bO2nioD\ncJwjTnkUxfSRbmkiMQ170EPhddg1u6h/06UV5gxVSYYwBI2CfqqEEqFcbFWc\nvNc41TsewFCOLK2R0hZWB1IiugbnPZ26aXEuH3IAWNrJC6XYJqBdlASgPWtf\nAqiq\r\n=rrDM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUkzZuei2+rgSLO6P3qS4RQQ0CnY9CVoKtcG0gdMOE3QIgcF993JiIyLosPUJZk0QHmjo/aNTvSA2x45b8lv/eHrk="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.2.2-canary.61.7e255dd5af0963b445c3df1f6de5bd869fd130c9.0_1606998398412_0.3622146807301809"},"_hasShrinkwrap":false},"2.2.2-canary.61.6fa0c86f0e801048de7559f882ebed9e26c2ef4c.0":{"name":"@sberdevices/assistant-client","version":"2.2.2-canary.61.6fa0c86f0e801048de7559f882ebed9e26c2ef4c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6fa0c86f0e801048de7559f882ebed9e26c2ef4c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.2.2-canary.61.6fa0c86f0e801048de7559f882ebed9e26c2ef4c.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-P49m+sMdF25eyAS8Oh3oZPWR2bYRSD5Mg/ZC0McHLBP+gXQ4bjjIlfI1vHwkhL4o8JIpNe6FViVdvG0D3sERAw==","shasum":"dd0ba2c9c4442896c433f85322b40511883f888e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.2.2-canary.61.6fa0c86f0e801048de7559f882ebed9e26c2ef4c.0.tgz","fileCount":60,"unpackedSize":1530474,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfyNmhCRA9TVsSAnZWagAAyd4P/3hdTUE+zYGE6sMpdxQe\nmS1XQ9kdOfsd6uq2/JpKAYhoqo5IDNvNsGwUE+L9e5g5tKyN0TDyoW3LcXWS\nlRUExcWdufuFqP80utVh2rQEEImBH1f0Zjw/kXBAMuOiO14b7kKTtyLtTyTT\nJWYm2NjO/7aT7NlXBqUDkrGrAEx6JlW36KnXs6vuvG+94Zjz5QXJw9eW97XH\nsZ1weszZ936HkcA4H7vg0Aqw2LgJlSrHAc81KjDbkU9CkB6giat079g+siOj\napWSJN06Q2+XDohscwG4hAtHe4dnj0W5PSQfKurje4gE5Qt2H63sU5jNO4oK\n2Y/dstXmMLveetsTg4E2LSdvbb3Z3kwVykB0zFN5MjuW3yYWNya8PXr7bvxw\npjPKPLPN0RRga//+b9pJxzkS/lDZF8oMkUBkPxJlPcD5uqmnBRCF6oYr3Iiu\n5jhDKb+TSA4EZGDT+QdKsPGJef+WJKF/AhK3pCi9i+rS0b+QBz5lCMN7Qqvq\nO6XiozD39+JxoOtQQ0Y0m98Ft58FG3DZKFanuYDQkcLeWcBeCu8kAm1B9ceY\n+jumWan50P/c9HDYAd9xBmiGxrmqzo+uqz7+XmzM6mIUf1+0QTcuio0vGws0\nn+FzfvtcNlSK/kGulLGF0T00PVoMFkK9KPpic934CCl2vcEXFEpQEDSyj56m\nCilD\r\n=EBqb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAwVOrJmOjkXMdADpRICbspDTrNRHmasdQ+5GJjUX1fmAiBgMDT7v/VjclJbkK16pYX1UGVkGvOM61bVE/5m6AxYqw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.2.2-canary.61.6fa0c86f0e801048de7559f882ebed9e26c2ef4c.0_1606998433315_0.7580874103529565"},"_hasShrinkwrap":false},"2.3.0-canary.54.57b4fa4d7264c6071f574dc1dbb73735dfb2fb1a.0":{"name":"@sberdevices/assistant-client","version":"2.3.0-canary.54.57b4fa4d7264c6071f574dc1dbb73735dfb2fb1a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"57b4fa4d7264c6071f574dc1dbb73735dfb2fb1a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.3.0-canary.54.57b4fa4d7264c6071f574dc1dbb73735dfb2fb1a.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-geUW5oGA4F5SKL3FCDovjQ8E1Izh1nbxAlXTRl/nfLOhGUu+8gcZYDYG1UNrfGLYT+2SnV5oCuIv8Fkl+66RDA==","shasum":"17b3efcd32bf27657c448f3892fe1c2dc1c9a212","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.3.0-canary.54.57b4fa4d7264c6071f574dc1dbb73735dfb2fb1a.0.tgz","fileCount":66,"unpackedSize":1556677,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfyePyCRA9TVsSAnZWagAAISQP/2ZemX5pHjNj4TLqMVBY\nQWzyYmnwo0FsSgBkAPOpNTVwqb3wmVWGJFitEIF04qEbw2XlNdPIKUGBO3qY\nO7WaO/TJHZgs57Wo8CV///1PA0FZh8uX4RNhuIPTE10OwQ7tsEjlqjqzMk/N\noTfH4Hy4//xbMl6cO+hTTzu8+YGvUQvijyh4ldnF1K+c5qoPmfiPJnU3hT4r\nNA0GZabBVpDMtSV6NUEjb3dhLYcoBwh0pkT1pKmGrlb0TW8aL1OZlGa/jz28\nHobi+gIiN1d2XsMDEDo1MCIKLql6MN7q8Fxp4LK6bJ6GHyoSA1BNRy5EZFIy\nInnZvceukjU/jBd9ENdeIG8wkze/0ghNBAcz5geICAlMODrhVrUvCV9n9eF2\nBnnn+KAh14LugdC4NUcNHCAkWEBCYJtKC8eMcOxX/VIoaVlZBPr1grzadziS\nY4TU4wVQuwjCc5wyr25GHvQU94Kyr91VjJM8fPcgPi6CuZZUW5wk4b2606X6\nEW4V2lXzn7pQLD/FarqSpVY5f9BzvwfgpQtlFfTU6eBqLG7mJcsJj04jjV3e\nRm1f8wAuPKl9onO425xURCHHttyydU99/SUHdaKOI4sIasF2vu8/HkOutpCQ\nKL/9ppyjcBx8lTI63bMh3cgBG5SGYPpaksAPjNJVThTGHdg/gFuysoDbyTw3\nu2U7\r\n=KuUZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCUWhuqhJb72oTtRv3DIQnGswyN1YKIoFx4NFuPO9SZAIhAL3ad8EtRiN94n+tASf4TVl+xLfsCfJ11gScdpuNlGzz"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.3.0-canary.54.57b4fa4d7264c6071f574dc1dbb73735dfb2fb1a.0_1607066609823_0.9941825034167451"},"_hasShrinkwrap":false},"2.2.2":{"name":"@sberdevices/assistant-client","version":"2.2.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e0b80642e4a6f87f68ec86d120bc3a36bf16d453","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.2.2","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-nmWqjejFOB8Xi+hz0gSLlGPKfsDzs0YYKnSWJnhxKxMZP8xq6tcvMpJ+/Bz9WuKpadvHl/CCdbfyHMIuLEnJDA==","shasum":"38e76253d6a71c7d410a7fcf8079175964a67c11","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.2.2.tgz","fileCount":60,"unpackedSize":1530713,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfye+XCRA9TVsSAnZWagAAWlgP/AsTKWcywPsBgo5UA5sg\n0+Ct38Lu5yXW7/5Wc0jVNB1U5SXvR6gmyHHgucBafqkNzCYDaybNrcytqNpX\ng1nfOhsfsE4LbaThrb54HbnkjsxCrXHyei25T0t1lUwr8M1iGckwDg8rdYW5\n4V8LnVpaiAQDk6D9l9sq28XIokh0LUZLXlsIVpOPb/V2z58kU9wCjLmRJuGg\nDolgXNN1RaJoiV6OlzwLTqj7G4LXK3ynO/lnmrcE7Poh0KKjrnZsKs6HvWtW\n0YD9cjcs7aohD0hUjn7bwd5Pd1WmCZA2df5464TdrwW7sImW5eMaADkUo1RH\nFz/uXmbY5/G4qSOzWlj17cF3AyTrfWk5KFUg/mQTqSo+uYueMbp7rW7QUWNl\n+1i5sozgytoUKnIXwi/IaRxq8jQaLjkVoyC4qJ8b72X7jzhT8Wy+BCDwxL4X\nOT36bfC9TegiZiGYkqMad2S+Y8W8vA2EwbZhH8ke/ABW5HYxSd3kwR1AUozZ\nwNSVbZJgZnYNzgAk1NHeilrQczLFcDYvcVUe8gND4ieoDm8aoCG2Nl+4wzDI\nWqN0LVE4gj7gosjf/LtugFJOBKMxsqNmSVF4wJVP7Td0a8Rv909fj2UuNmFR\nhVThOxXtuRC2spgH2LsJBvnrRucAsyp0+dAK/9Ymv5oyK03/ufytZlMd3zkO\nbK1f\r\n=GNah\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBSDtJ9+RFeBxwTpwT8zmUuy5hZH91mcK8qiEEUyYQugAiEAsiZrOhcvFgQUvbFEdnjM9Rynw66vZw7VL35ljJY8K1E="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.2.2_1607069591147_0.31585556149900995"},"_hasShrinkwrap":false},"2.3.0":{"name":"@sberdevices/assistant-client","version":"2.3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9d201a1d5d6462bcc486b7c21fdf04c7388a40ce","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.3.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-+Do3GHL4tKuaDreJfrghObK+uRvDG8JzGb65mi+jH8gUyqSaG1uHvPlsM+bsTfB7WydWHjbZiwS4zi89rujquw==","shasum":"0a12acb0ffcb1764b310a7ef5c0a42c964ce391f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.3.0.tgz","fileCount":66,"unpackedSize":1557473,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfyiQmCRA9TVsSAnZWagAAsnwQAKQKSg7+Le171fB9o9qY\noBSeejyInJnNeZlsOrAySvQTzux0o3loCOnZHjAkdimDA+BnzjX6Nmr3Wg9N\nVa5qwCpV3HW1Q22VrW+3kt8VNE/Hct5T8jpXLvMNeffz2DqDCbLbVSYPQHGa\negSwMM1xzxy6yYCBDrumBHjM9TkRMa5g41uKFb2LX3Efc89E7S4CG/eD2wEC\nzhKOuF2ULEDWoyeOIxGiilIcMnWl203RdP33cdOP4nK/OUnyMI95rgYnEHf8\nUcHujOF+q390ka3FSLTa39ObVHhzQUolHNZndJCRcNtvEupDPrdy2JZljxyB\nZX9aYUbrz8jYhGuAcTgBDQ8jHb7/DgG1qwPBUkrQ1BMRnFRRFyVwX78AVR5D\nNAzZAM+iSOym580dkovhWOAP4UKbF4NudbIknIC1LNOkNCi8oROEXb+y3kue\n1UH4YAasWOb4X5RgLzoia/1uVQ+k0fbZ1oi3tAwy8iDsGxyawD38FPymlakV\nK2IHwmkbxmEGVADcXeeT8+ki+e35Sjp9crHiHs0es0jehl2prVr+aopSLr6U\nwNYhORVAUogmnleuSz3z9FOyYrYABQuyBa24TtBEDW0gJ5JRb7e7Lh+5Hji5\n2C6TvxAweettiWLHVqo/i4FwvXGEqYmJ3h0Q+J5AtJku8s/e3pV4FSN8cXTL\nVX5K\r\n=Hu2p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCovkaqg7Hf0FbqmOsZ6X/sNhuopqHmjJzCJ8zOjIQL4wIgUhMWYXQpkF4jfmECXl9OjYs/vXRkGq3mGBi1lPAEvPY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.3.0_1607083045891_0.7154397982585865"},"_hasShrinkwrap":false},"2.4.0-canary.66.c7b2b744b76765d7f12017a5b2ee7da64042847b.0":{"name":"@sberdevices/assistant-client","version":"2.4.0-canary.66.c7b2b744b76765d7f12017a5b2ee7da64042847b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c7b2b744b76765d7f12017a5b2ee7da64042847b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.4.0-canary.66.c7b2b744b76765d7f12017a5b2ee7da64042847b.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-E/8vh/7JMnoQHQaADC2ZbeNtEAKXpEI9VfVRxNB2xRq+oKPpS1fbaz3jKHNIkAwA/Kassa9fAFYOGPod1VuA8Q==","shasum":"bfe041f946310d6302937520197c303a7e3e2e42","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.4.0-canary.66.c7b2b744b76765d7f12017a5b2ee7da64042847b.0.tgz","fileCount":69,"unpackedSize":1563974,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf0Lv9CRA9TVsSAnZWagAAIX8QAJorXp2ajeRs25/Jsrwz\nHxft0xbp+24PkdjEyS7O/zeq41ceo0G2uXJ8NTj5RLwsOZ/WD0jjNW/IMc+k\n86SxhwSl3EqmYhJq0znD/PcRQ06vMvk8QrIFUcl7x5LRhpNqD2Pyj7F8zxYp\nP3gYI8kOj+JDbpapwyyUdwn8ihyBnJl6Pc4YFxT4sAwwEdd+XjcvJK3FyeIs\nTiBxUeKM+M8jTH068pnJ9H1IkPpqLAGZ1xQDcX8hxoY0yogfO+F893c5lnfJ\nA3xKE2IpBzgqbFTKD57i/+F+AeSnr77QVpOOJOm+at5VzLffxpvlH6tR5qyo\nxPY5Y61XTkNcV5tqy9xGDbH4DmKXPR2Qikefi8z3N/dyRVPcLhQMU87OjhzG\nM6bJ1cpBcnNr65gsTeq0sbGsEUk43GaYxVq20z9zL9beomjWPmxk4Ln8M6Up\n5CTsDwri0mViTV/kQEK54w+a39bWFZEXrNwcOBHp4KraX+CZN6lbFPgCdISZ\nVZnHP17EncQaN/TtAhteS5/O0KAufqRiYtDfzZbyyIAM89jw0ZuT6Af/TOVs\n/92LfBR4YvMxqh20vRmHf3nNJusisL4Kch4tjJUEdMhTN0WNaTcS2I0o3NEg\nZUEQdMAj25fUG1PqTBeaL9Te7KdnCbCq/DcF58DWoN9D4V+GYNUM8iQ6YlIE\n2k9A\r\n=opsn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDIUdOa/wIGIzaRLH3FI4vJms+HskIo74b8xFUKvbGByAIhAKB83QNhM1I28kp59jRQCijialYRMT5AX6gamueyeopx"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.4.0-canary.66.c7b2b744b76765d7f12017a5b2ee7da64042847b.0_1607515133065_0.5555098443356157"},"_hasShrinkwrap":false},"2.4.0-canary.66.9cbe72cae1f80086cd3a63695f4056250fffe5f2.0":{"name":"@sberdevices/assistant-client","version":"2.4.0-canary.66.9cbe72cae1f80086cd3a63695f4056250fffe5f2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9cbe72cae1f80086cd3a63695f4056250fffe5f2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.4.0-canary.66.9cbe72cae1f80086cd3a63695f4056250fffe5f2.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-nLo3oJpQnRRdkr4jjeLKe4j47Slb3CGArpRnulr5G7/z4fqeXbz6HxjvlS9fPw0fZnrY2241UUYi+HUiYuEbmg==","shasum":"1c68f199cc739999d6c1be8b5cd9e869bb17d19e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.4.0-canary.66.9cbe72cae1f80086cd3a63695f4056250fffe5f2.0.tgz","fileCount":69,"unpackedSize":1563974,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf0L+bCRA9TVsSAnZWagAAOUkP/3zG4mSdvmLRrAZTVjUn\nLpyvKSuNFY9FSoOJJiL2jqelFRCb9lEvm+7d1HpvxFDHWhIv6xVzNBuBStGx\nZimPNPb4euXpbmu1RmGgJEAU1TVs/fhawgsvAzrjY1wpz5kWJJFnYCr8yhEe\n/DJmzwsrNikQnQ7LJi0I9wbWmmPxIjycYXjjt0o+KfiFHBi/HWT09hsyvASS\nwJNT3AmHduPfPFw8nnIuyT7RzHlB0t2JStU0D1b3fIl2/+vJLPUOiiMribNd\n7qXnNooVsh2Zkhh2pkFBvfpZ3D0LwoFDH7TZlSQd8xm43jrQmUtj0JTOUW1a\nTSQiAVGr3cp760knPjrHXwJyzVzwAdJfojydhCvNVUWFhfEzI/Dk0lFCSsiM\nK7lXq36PbrHIM6RF4JvY+Cae2oDGMk6QxfOrUOC+wwdlxqoHrFVubCAaLp32\n+a/d+/e3UEJI0Na7+l3+2sFDocRra6i816yYdc9PLCUbQSjnKbsXIEsELX2A\njyWwXAjLlXB33qmGQsilpU48D1TGVwh5gB6rByKet3+JVKALfvGkJCSvuWAo\nYpYf1jmZTMeJz045gDlLXJx8mDC+WAEBV8k8Dl6Ha56jpX20xgIZke7B8t8q\nZ3GE9Y28xFCS2jFO1PHyBU6P+SuAmChObXCnfaXFgx8fs43AYFeoc5lF6pGG\n8KGs\r\n=xYCM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDOoHuxsquDWqKjT6JSo5y5cwb18gWVnFPFuPGAUY7ThQIhANm25/nWzA9k9tw8/ohuN0XAYsFwJ5Op2jM+cl09+RbW"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.4.0-canary.66.9cbe72cae1f80086cd3a63695f4056250fffe5f2.0_1607516058661_0.18097950409377916"},"_hasShrinkwrap":false},"2.4.0":{"name":"@sberdevices/assistant-client","version":"2.4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8e2d395b97064da0e1f074f3f3d4bab1b1f0fff6","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.4.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-YgnCVcn2iS/UGdkCxz/7VfHQag8WYxYuQY9UC8Jj3Sx824kBF2Dl54IFeeEZqAoL+0dqfu5oX6oZbvyIiFT2qg==","shasum":"1f83fecbdd70562c4348a5ad9f11bb9179a47f5d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.4.0.tgz","fileCount":69,"unpackedSize":1564789,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf0MEJCRA9TVsSAnZWagAAk1gP/A0BsVRJQy1zE+4oVtKr\nJKHoYarIMipLNuhJJ7U0nNStYb0xGe3+I7czbYOPp+FhxtTcW899uZQytwnv\noFiGHUlCMyP/mTSXEKbZpw+sX8s0I/6HjvXfnO775+RbNwNHAdlq6wgvZ8BV\nGcd6yZTwyue8h6VMdh/sRezilDrauT1u3wZjgn7QvceuBNYB93LAOuPXCiKt\n4FgZGY2rkdQf+jFpAk2oZApTbYxxUZVvfyGTLL97+fUGmsVGqky2URUJD/9b\nT5iryfacl/tzHIXSpm6WLgc0vlwBAC/gNs+PX795xBhq3TjGxLspVpTw8r9s\nTJaNtERz1d+OlSG6VFEcaj2XQtcXfXyNL54qw3UIWjGyX8x4EkSgXtAbHqE8\nZwqXD/yLDO+whHco1MVyJqQ0roWGv9Ew73Hnpe0LKvHm9y26bFRHOwLN7H0H\nAdOCmGynduvxUlCSCdLTRxdXEEwosMUo/4tjWBBMCMSa21edzd1gJudhBIha\n+A+xG6r6eoSjApcGV5732bcEeTwCku2VRMku7Da/caT5FqtE3O5oivkylHpb\nq7qDQLNG+xJOVjdqAhHhVFI7nHDdXwYIcPtQ8eUvG2sl4TEdhJ1Bt4NAlRs4\nwMiOuKzCqVz0iNlae+8dKwt1yTUpjpNJZVKtbX5VdeGzufiOi8Zf8QhsJT0n\nyjfp\r\n=pepU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZJlbll8ItvsQO2ziqXrURRVPXvdYAIlyt6Pn9piewxgIgH8AwNVHhptYjNgtHsIz7Du/kS5gkFJdQQ4/IcJ6cQiU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.4.0_1607516424684_0.5964478174515653"},"_hasShrinkwrap":false},"2.4.1-canary.68.9c1e7e987e4fed8d01f7b732e1e738ebe8cd252d.0":{"name":"@sberdevices/assistant-client","version":"2.4.1-canary.68.9c1e7e987e4fed8d01f7b732e1e738ebe8cd252d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9c1e7e987e4fed8d01f7b732e1e738ebe8cd252d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.4.1-canary.68.9c1e7e987e4fed8d01f7b732e1e738ebe8cd252d.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-zthj3Qhz5b2azR16Hh9yvR7BUb4GZY6UVUE7vpMeCYa3RyuBD6qMHJCjwdWPXwdTvwIYWQjyAMBm38vUkqLakQ==","shasum":"92ee51f6fb832051bc66334f3c295eba6d162634","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.4.1-canary.68.9c1e7e987e4fed8d01f7b732e1e738ebe8cd252d.0.tgz","fileCount":69,"unpackedSize":1565021,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf03YoCRA9TVsSAnZWagAAF0kQAJTg38g/3XpwO5B/xblU\nWqekjWkJCJ+JXvQtgp/o26m3taFVLPZXbibaiIZCeNhmuW5XrpsdUO6k6iF/\npEikS35dtYp1tw52QGXzJYmrToN3/4sYWtdbTtHiYnXaliCHyYZl6LOa3+oA\ncQpkJXgjLs9sC7ncjOyAgUOdKbJE7mDupKAWd4ma3ak3V23Gm5L3IvepWIrd\nV0MaDJrq870h3lvABvaNK9pmDQn/RlL34KOZ0Gh4N3q9s0eNfREBkPioM4/V\nNJ6zILzerJLZOW4sJpfOBwoi6WpmNnpjp1m8/W2aSB/DYv9yeQ4rY4OhAsOp\njjhevLAJe59Dz4rLJQVsGXX97Wc5N1H/MPkJw9w7c75twGv1TyVVQoHM8Oh0\n64JHlm16cQ2eod6Iy5fodgd92ynW/aZDfBe5cmY7Z4tTbAPDe/3Q+BeeYghy\nNupaWNqPWMzUmyJXCXcJfVAwb26CJwEGdIt+6eCWyoD1XjgHfjMZKOH/3+QG\nqFw8kJMqfPuj+F0Ulzb1+mWKaZEkhqQ6rFvY0I1szXzjY38mKitGWXTLI0t4\nrXNJ1qwgK/w7FDMdcF0YNI+y7AwOBW8n8VMPFzquroW+WzMJjLlzY7bDD2E3\n0tdD12TkVIcgf0KxASqs2EO+JJS3rhKearwIY99wTYbqMyUCiNt2oQyaSbhq\nCMfC\r\n=WHG4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG7noO9je2vfbexX/d4GGMICyuV4T21evGQZ7X8QY0r3AiBFXa/2PRxblpoJJxwynD97AVs0EiHyO2Yqp3j9xe9O+A=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"awinogradov","email":"winogradovaa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.4.1-canary.68.9c1e7e987e4fed8d01f7b732e1e738ebe8cd252d.0_1607693863248_0.19875211526185943"},"_hasShrinkwrap":false},"2.4.1":{"name":"@sberdevices/assistant-client","version":"2.4.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9f15b64330033a8fe5fe149f94dd714a6d26093a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.4.1","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-UdbGGKFAxpGdhbhWPoE1q23YYeyYRHKp0Zgxh5OVJhexUOYiweSXVpUr1+ecj6oJAAmiKhZybpTSlv4FPB5i9A==","shasum":"1fdb08f49f189c0e9f38a4d9ac6cc0903781c6f5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.4.1.tgz","fileCount":69,"unpackedSize":1565434,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf2H4gCRA9TVsSAnZWagAA33MP/168sn4povwH2g0pAdF4\nT/SzJ36L5+yKq7HZqUcaz+YNZylCwyDJFr1nK2hFZvmD/HAjWPf9Ge3IVFvH\nWOLrZW65OwNyX/MFibHCavppWN71p5+HF/n51IkYLjeEkPw5hAyzBRHMfpJH\nEjCQ0iOChINF6OXvvxChyI2h1b/MDOb4uRv5IssCQiIsYCJBwn30PCDH0+iw\nQcKgLJgt6cwbuOlyePd16I2vZMrwObGwO+tUxq7Z7pgIuqVxHGkqmBUoC3Ac\nkNOElg816JS+7wGUk2RfgV7czElkd6FjfOA0kTMZW+GwEQ30Jv8LfRC9ZMby\n72q0k5myecLale6s4Elhf01PWdeRsRPLpczS2ywiNADyNpc6bT1h97QBTS5U\nQ2wPB1UK72v8jH5+bjgxNFDBpajC3w1aFVItXEQZGjVCsoE0CG/BL7yAUH9O\nq6xR1P4AoyS+mjeeQyjHZdT7KKjwWt1I425lZUKfky0UaKYumDOcbO3APHAC\nrCgJnfZwiQ1BpXQAwMJPUWcaKau8eSAlZeFfsDDggFo90gLMlqt0LiMB2F+l\nBDTT1M4tJE4KpFaZ8Nk4ecY4TxIYNdcyns11IJy+JVbqACueOZxlTaVnnHoJ\n6R0hJRl8oyx0BcN/erF31ubZPgBwpFbsmIal4UEvfMQlLx2JGaDRk0YVnD0S\n0rcO\r\n=AETx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDNFc98G4PQ3btz8LANALc/JdESlXtD2MTPFO0HLZqnrAIgZ2Et2CWYERRjVqbujiwdT6nx8t+2smhtLDr+QabQeUs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.4.1_1608023584100_0.2513211255863632"},"_hasShrinkwrap":false},"2.5.0-canary.69.cbe3992c562bc2071f2b1247aa88c74fa1039f10.0":{"name":"@sberdevices/assistant-client","version":"2.5.0-canary.69.cbe3992c562bc2071f2b1247aa88c74fa1039f10.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cbe3992c562bc2071f2b1247aa88c74fa1039f10","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.5.0-canary.69.cbe3992c562bc2071f2b1247aa88c74fa1039f10.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-9yVHu7QX6Utx/eFt1+kn2JlX7J2eYJC8/vr5vlz81KIIH6/OaOXj+rX62BkCl+YelXZSEI00vsGng3ttyMG7Yw==","shasum":"5461b629305c27d04e970282983704ba1297d6fe","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.5.0-canary.69.cbe3992c562bc2071f2b1247aa88c74fa1039f10.0.tgz","fileCount":69,"unpackedSize":1572701,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf2IxeCRA9TVsSAnZWagAAJsoP/1hsf3kVDEIqfNJjBmbv\nOkWdA6r3trLdpWaBRwthJ/H1C3TdzKiILjKLAlme616itye3kksLU18+sczX\nrw0+L9KzVOnEeMxOM5lmSOAVYJvnZ9oZnS9n5ChdSFjxWFYVcE61QWwq9kQM\ntOC9tYeVvCaOpUaXg0VJ1Nu0wb66GzwwFGah82R+2av2X9OQF30REe0f+9os\naS7C++7Rn15e4wynNFi8X8okoGUouDqT9lfzDb7tjgpNkbuOKdsWCM+tMRab\noNoUG/jWXYGiptH9GySiQB3D2acfhUJpjAmCXLQl8cyio8tzdzIjDJQSPgdv\nSQ3P08aukWFNrjnUduRDEcDEDB80W/3iKSOkgbz0u3UHu80qvsgvK6GPaJy3\n3AXWVxKHdFVzBqaisxq/ARAKfbZRWBM9fGgrn8JcOHza2lKGsd2uyYlMO2d+\npXKCVO9+beld/UFC+YhEPKw9brJaTFVm8SG5UZ75hvaQL+Bi5KLJyZHfDQx0\npJ72t1hNJjWOlfNwjLg5H50zH+onq2PBvdo/fW6V12j3R9odEMpebFciNzNL\nftmlEvxB52YqOIjNzYVhBadKTletqQZxorzbBQRL6AeqkTc7NMNHnqKvpAMc\nu2/4G7wWclLMKMJHoRsDN3IxWVnm30+FJZ2saa9e0Te/nyPhhPOl99+EMXP9\nSasQ\r\n=MYQB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCI3cnRGibjbFVLjeBCMLHtnw1iIjNgoh5Iqhqf5E0mZQIhAKZyYYgf/zTzbWT69NKZmhChHJ8UFeywJlQrHkvpJ61D"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.5.0-canary.69.cbe3992c562bc2071f2b1247aa88c74fa1039f10.0_1608027229699_0.59370804726809"},"_hasShrinkwrap":false},"2.4.2-canary.72.f61261ca3bfc7f81c3b27a66b52c2fabd0b6b1c1.0":{"name":"@sberdevices/assistant-client","version":"2.4.2-canary.72.f61261ca3bfc7f81c3b27a66b52c2fabd0b6b1c1.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f61261ca3bfc7f81c3b27a66b52c2fabd0b6b1c1","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.4.2-canary.72.f61261ca3bfc7f81c3b27a66b52c2fabd0b6b1c1.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-xqvXocNGZLletwFAtHDC24Eu1Z9Bcu+lB8IpVhIbAjoHiQVgKHZu6ZRbl34JZw1ZcdtnOMS3waG4o19JzXl1gQ==","shasum":"98785cb98da0b0b642ed0731d0b4fcedfdf2252c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.4.2-canary.72.f61261ca3bfc7f81c3b27a66b52c2fabd0b6b1c1.0.tgz","fileCount":69,"unpackedSize":1572864,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf3KchCRA9TVsSAnZWagAAuG4P/3SsYszo1yRXYloia0Q9\nLiCqa1YwDkQ8GlHqbYsrz4pEsR4vaA4ir4vN0ohGPPJzeqpephiozxSavwjV\n9FkEH4JXd37WOyVI0tAjIL0EpIQmomLxKMsqRdngkgipSQOd3+KxY3pQb3/k\njmkmDZgfElb2mquwvFYx9IyyX/EzTEWgjmDWwJmltqJGZhNvfGs9w2xupkdY\nEPegOU/yMhR0b3izbMRekZARRXs31MkPFAkstFgjNXwiN7SwfPgXV0QtaUCu\nFTTqbgAhWPULckJsO3WcG8716mdACC8sIU4KgmRdaAM8/P9DYP7rKMThlEly\nT9c9MP/B5WwOauOgO+IvJVkAx7f5Sa1qMLnlII25OQMgnBxwSluXw7jM4Vnj\ngcCgDjnZrIcWlr417zEalVbrAk9NH/hgvedLd9mgWuf9irSTENimh++Ffn/2\nz+4IkgCgM6Bc/T9PlAhW39IA/d3IwyFl8Nx5dc3i62BWQZIMy01TCXStnOKM\nZwhYMJlG9OQr/+47T4cSisO4L2DD74V95ljslH4ieh5SG3XxRBObWGvVayTp\nETlLxjJMJ4C0kaRR8RP6EimjElZVYI/TO8SHZHfyy27liS0BxqFETf1YW7f/\nHHMdm19bZt37KVaffsWi01eDydebt/yhRHuJYAeJ95WOy4z/F6Ekyt528kJy\nfLfH\r\n=VBuU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHT8wXp89mtz0THrPSFpsD7brM4XLRN7gYtrhp4omGawAiBhF8aF7cIw+QvZPOK3I1u35tkQGmaFifqRUKnJwecSwA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.4.2-canary.72.f61261ca3bfc7f81c3b27a66b52c2fabd0b6b1c1.0_1608296225427_0.27614904624459236"},"_hasShrinkwrap":false},"2.5.0":{"name":"@sberdevices/assistant-client","version":"2.5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1f6133c678c21b4a06ca402e67515dffd21dab4c","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.5.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-sjd+nBAIA0S73SGDBrQ7V4Op++Ba0IN8Owczzb4c/lCfevGScZwEr/ff7ZabBt3k9n5gPtF8y42HCjxpE3R/cQ==","shasum":"b261b9a64f4c258bb609145afa3091fd2226e50e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.5.0.tgz","fileCount":69,"unpackedSize":1573939,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf3Ko0CRA9TVsSAnZWagAAPpoP/0bxUDpeKwzKkMiEyiGF\nVvL/teG999R8FXkVm+wTvnBNWhfg204225JVPTlw2D4P9QmWH2OgXImAnHX1\nmxJNOJ36VaKUfMSAvLp82xmzgUqdaCzBhy/5xyH9J/NOOFQAEJAtCz8kVZyM\nwerOJCtdB3xoy0GuWLXu4h45ZNvg+PVXV9lEAcH8yz0jYjuHdBcOyz945A3V\nPzdPB1Q/IJGsOWdd+44Is4byHprJ8fJ+5+Cs1LtyAGT9mMtlj/niiOL3GAmD\n2ClIlyEVSIwnatq9g+p04Oop52pcTwfddHUeOY+TjXe6L7U1MDNAxHRRaF0R\nXloNSsxUyXO/V776uUZonad2jRP9WXsfZaXBYsocwfTGcs5t57sI4wPmFE5T\nnBWbCKYavYDc7RsX01VHNjHCEbElXudVPV+6QS6z+cWdZAL2uETsZcF21JFk\nojTiMvvj1cTHSCBYH1hCEfrF7oNe7oOaJP5qSZdceCFpzXkrem8KJeIDcqFZ\no60at3pRY3XalrRf2XPBxEeIH2kBZFclOu4ONJm+czYz540pUJ2wsXiWsp11\n1RqdIijc/ODndatgw2rK9Jt6XROjPV2yixZPtu4lns+8CnbwFjFo4EI0Kqhv\nEFSwOroiw+u9Z0ZFf+Mcd+KradJk6zl9wJaNbWqMeEtQoZln9cr2jNHE2J8I\n/1Wh\r\n=/cUm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGPWrdS1obx5PDd6lczZkoUA8tKIZ6gfGY1IA/hwqK7PAiEA5s6UWVL4s5sJTiDldtU7+ny7b1tU23ZQVZlmhsxz7KM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.5.0_1608297012135_0.011422657844964546"},"_hasShrinkwrap":false},"2.6.0-canary.73.5f3fe2717895d24dd7b02404eab6d6b4948ccf33.0":{"name":"@sberdevices/assistant-client","version":"2.6.0-canary.73.5f3fe2717895d24dd7b02404eab6d6b4948ccf33.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5f3fe2717895d24dd7b02404eab6d6b4948ccf33","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.6.0-canary.73.5f3fe2717895d24dd7b02404eab6d6b4948ccf33.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-U7AH+Omup/XiXlcGlGslYuOH7VghuOal2UZgVVVoptSVnE5tymbnAsReNLUlsIT4zxmuDcVCuLs4p7gfE4g4ww==","shasum":"65847cf5943f46eaa4f08e143a1dbb13f38db7f6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.6.0-canary.73.5f3fe2717895d24dd7b02404eab6d6b4948ccf33.0.tgz","fileCount":69,"unpackedSize":1574644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf4ffrCRA9TVsSAnZWagAAYVoQAJkP3soHiMFKamb6jgGn\nQGDJ3TTqJobW/le6Q1tfFRt0Ey/Y21v2rXgIg9fV1XaJ2I9AvzA76ziZu4xh\nB6cUPLEUfLSSxRp+D7vgPTOS0zSmT3SwnpHv9rCO8nVP0B0v8RoL+mn8ArCe\no2+OQc0Lwy9NetwUXwiE8ERv6y2JZ7uv6+l3JQJPNOjEpHFp6w76ClSwatxY\n7qd6bC4J2D77U6xIjvVKHv4FHC7TVJLodbdC0Rx5IoPMSd9pBZkRA59weZUO\nMJULSKS7OiAbbpB6wXSD1rP+S7B10swFFi3YaRuyacfhybvScvSRaMi2vL9M\nrBPst3Rr88RgcK7C4Zl8NAmxIdtLlhNO1T9C6h9Bu4blk9iiK2a4HKfinLQt\nJQ6nrFZKvUw3AuOAfWMAvpe48RA3hIkLjLG7a+PePpll7fknDxmGlbiPriae\nP9oemm/w+2sNaSm/YxMZbvNlc0keHym9QgEUa9AD1grPTXxSn4aafj7j5fKf\nIoqRYAvU6es8eMiaYQLHAZ8y7/0fxvA0wnUVZzYO9ZkvtlYH7cMoUXxcy5s8\n4BKZWk2YVFkAmZqDKTTPlhMUfM48UMjK0xCl8PeB9jaUvPOvQnbXGKvt2GPe\npuiywkBHZbV6FLy989loMvTnd+il+NgEoKHNYE2Ndo6lywR9xtJvaJ4Io80R\nmwjV\r\n=yKOH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIElXjG0dnGbdrW8gIaEgMgHT4bJWWfxCrPgTV1kQdA6/AiEA0x5gZNddJDGCZCBpn4x5rxTc3A1spxv2qz9WXyZOZUg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.6.0-canary.73.5f3fe2717895d24dd7b02404eab6d6b4948ccf33.0_1608644586547_0.8096947156256511"},"_hasShrinkwrap":false},"2.6.0-canary.73.32a4e319535a88e140d62f8431916719eee4f971.0":{"name":"@sberdevices/assistant-client","version":"2.6.0-canary.73.32a4e319535a88e140d62f8431916719eee4f971.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"32a4e319535a88e140d62f8431916719eee4f971","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.6.0-canary.73.32a4e319535a88e140d62f8431916719eee4f971.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-tuFm9w05CGYR31honExU+7h2KSnlPhCfJi0DrUxtscyjTlDDlYCLY8Iqung6XyvDlGJjSSEQXo/JiK9PvKsJZg==","shasum":"196beaa515141f59958aa8f2bc1eeaf5810d76bc","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.6.0-canary.73.32a4e319535a88e140d62f8431916719eee4f971.0.tgz","fileCount":69,"unpackedSize":1574774,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf4umPCRA9TVsSAnZWagAAhBAQAJLkdNJnuQAikXLjILLt\ndUjBEDwQZgHgO2DaKz5VhB1y7y/lp8y5DBGcWa3UtCRqSPsS8Waj5cO/e5GO\nrpmb9GglR6LgJkKsh0RTuwbNf4fnTThRMgWtWt2Z9PWrElHdSiwAG+zYr2b+\ne1fLX6dUdrqUPf7iF9m59Yg5oQuXsavpPAQzd37ngscGLOlo6KwQZHtq5Rfn\nfnxjdF20E1n+G9VnT16xQFJDmszK+jWWP+qoQyRzI7bXY//xx5p8doyuhpy4\n/Kzg7WaaYZSUDzT982zeZ3hinL6u78EMw4Df3cnIzFSGtXqZse+l8wTkdcZy\nk3F6hW4JM0BRhBTskPA9hok15n/POfhB50PrNfS1OGUxeTBy/vlSRUFRkPnG\nyzCTH8jSShzbIxiKXQDicTjjp0EpBt/w2VyzNEzrMDeeZhUEfaVgJcQES5/J\ngsAlvSvy1GYZ5ZI6ZIbEz6jPc2vmaxp8xwotxR2/OTjBk5i39BpLeeGnd1hj\nl+HCkpxyeCx5juSwEoG7dYTB60TiAZt9uSRHaeWoq2VZ+vRU3b1crtfgOVl/\ngnAh5uFcdgeovJO3l4nUW8JQwBlG7b2+byfmC4VaSOZyGI4MqqpLaeuvJHyV\nDvqZyMkbtSm5aDpo7+c6tQHSOUM/7Qa/9BKflGL3D9yP67C538j8eng79Mym\niPGZ\r\n=X+0w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGrrrTOcC2UgWVDV1vOymAdt4OXXIwBZW3MIpGGLEICUAiB3l88aprA2MR3hXrNVckKIbP982bmMOrRGcw6DC7/nAw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.6.0-canary.73.32a4e319535a88e140d62f8431916719eee4f971.0_1608706446523_0.5705473074169436"},"_hasShrinkwrap":false},"2.6.0":{"name":"@sberdevices/assistant-client","version":"2.6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cf31812ef814eed9e0a9afa1a19b096b8025048f","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.6.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-V6iGdomV/pZyaxphF5OgMbdbBSxxbqL19op0c6N2QMrsvlpGd/eVys5txKBwixQqozYaKhmqvkwIkdvdsLr6kw==","shasum":"d8daae3a9484750ceddedf6078c8cbd3cf3fac9d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.6.0.tgz","fileCount":69,"unpackedSize":1575021,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf4w06CRA9TVsSAnZWagAAHjIP/jrtQxmbeVewvvDBu5wc\nJSxnB7AfLIBAfQXXi7Hg8PLRcwfEtjmejS9cP61ZQA/e30kKUZeI7tHw4e9z\nA76GVYGH1Yd/7IyC0K9Q5rZGbJGeeu4V1bVGlCgHhCr0aGPD8+0LsQNccHy3\nkrr74ITK8bMs/Pok3OpppqSW/et2eHqOvmyYm5pswHbA1ANiBGDsg06j44/I\nGQs4aeZsB2A6v3GXR9jWezyVBKJ3rZOZYOOl/9X4c3lHu4Ea8HEZBICPDlXa\nEzp+TTRvmceIbVAAxwnhJKRCb2JIdCmWkW7OIA76iZ2VZVqXWyxRkGAAebX9\nPK954xxEcHFj96p8ldRiJjHzJQauTXC/b4+JRt3/cCvxCWDbwT+ixtQod6kq\nBH1ddhkFpoOPnGJ1zpVaA/doCGzyMdWZiefbMICfFuRI2WGWCtz9FLMectoT\nibOG/ognl8ThGHKjVX12se9lFAxgG3GQF8R4DXlNo9ckgSK7qGiYujZzsliX\nlCvklVtHPsfFywYu7OYdffPK5QBAQjac2psjsdwBLTDWVo0GYtLp9uzWxDhU\nKYUKpry4ZjrBrr8zgxwXk0Ij9tONuM0Yg5qZMuNVq0qAyn5YtquBwUYobeIq\nT6p8zTenNx6WAOO8TiITIPQ4oo4zYA8PGTgSy85Lt0jOSTvT+6/dIZ2TqbAn\nnd5f\r\n=0g4e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDACfF5pQOLuFo9kWL3h18MQTqdj5aMJnvW4+XgejfhHAiB+vgBcqMaNlFs8Xya9apHDSPher/yztueESL3Ydqzm2w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.6.0_1608715578277_0.0918202249975264"},"_hasShrinkwrap":false},"2.7.0-canary.74.d8f42e3013406e708ea6c932439aa79a9e5b1ba8.0":{"name":"@sberdevices/assistant-client","version":"2.7.0-canary.74.d8f42e3013406e708ea6c932439aa79a9e5b1ba8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d8f42e3013406e708ea6c932439aa79a9e5b1ba8","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.7.0-canary.74.d8f42e3013406e708ea6c932439aa79a9e5b1ba8.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-uvJCdJVg9Kc7N0hpCzTTPp2dCLb1q26qmlUJvSv8cWTRAByITmVd0/m++LlYuq79kRm0IZMVo2f20gm9XWBXFA==","shasum":"078dd7386f4e01ff4eb5fd2bc3ff8f771e318ea4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.7.0-canary.74.d8f42e3013406e708ea6c932439aa79a9e5b1ba8.0.tgz","fileCount":69,"unpackedSize":1580350,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf6w2pCRA9TVsSAnZWagAAwhwP/iYL1X2RCEEiiee4qijC\nfCC361a9K3Zz3StXpkA7htFhB59XKIYfSQKxSu4zEigX5FLzzKa+UPKcnv5z\n+N4Kn8n2Tu21ghj1IcOj/iZonR68fotk2pUbPwDWeEZ7gX6euyB9Uh4FPYVu\ng1fM5HToIYfsstd3yiVAWpsRLoUXXZlAKkJlcPikUJA2Ct1IZF9Tp3ipTKwA\nWXxmGqm5gjRQ3Ih6+jNBW0mKSfR12Dl3RTQqeK6mw00id5m85gAVRU2EXyX6\nWnNVqyjAW8woxyxFdwbFdeVDByVuMFj1zD9lK5+PXUd5GI8S1pWRjDmLVO0T\n2HYFRFPlCZPuAnAuBNYtXSM5FafvMzh3wAOT9GB5fBd0M38F7K6HI92uAteJ\nE0B3P9wbE2OklWFVzWGKXVkaz2ZpReL9QeUWr4thwr7mPoM11qu933ciimqn\nxkrY1i8Cp+mrEpqZCJCY+6wsidnV0iNnzIUAocNv0u4QJz5+je3aPo8S4+jN\nowkPep9XDkbKfMNaLayJ2biV2EU+tt9NVgWk+Wy7Ivw25U/wWz4iQr/iDf5G\n0y7oyPfpv6DcVfmwdqOxpGhczirUwoz88V7XqZR75glv0+rkRZLMALcjsna+\nOWtMi0V5knRiyrNTIDlAD9hwluer+WFVAk0C+6E3TaO3nVT0+8MSXXMDscix\n10Zu\r\n=UjRJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDK8J3fZMsQ0/8NVaf3K/Pc3o7EUWu7YnVC+ihfEezD6wIhALZJwPi42x2qFvCXX5IUqKuN99rOZRbD+Oav+KYdEjYA"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.7.0-canary.74.d8f42e3013406e708ea6c932439aa79a9e5b1ba8.0_1609239976588_0.9909636670775226"},"_hasShrinkwrap":false},"2.7.0-canary.74.26d827d71d0af7142af7cfecc7d2a44d385cdb15.0":{"name":"@sberdevices/assistant-client","version":"2.7.0-canary.74.26d827d71d0af7142af7cfecc7d2a44d385cdb15.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"26d827d71d0af7142af7cfecc7d2a44d385cdb15","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.7.0-canary.74.26d827d71d0af7142af7cfecc7d2a44d385cdb15.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-RjOXRcthds30+QbIJ/Vunz7+CL1f9Tgt7Osy8ud2jkC9EW5fT9tw1FxdqtGqAqN4TrwdKkKz4wN5jKZL2ilU5g==","shasum":"1b93a05f5502f99a8d7b8d84ce0d39593eb6bb7a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.7.0-canary.74.26d827d71d0af7142af7cfecc7d2a44d385cdb15.0.tgz","fileCount":69,"unpackedSize":1579166,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDpQfCRA9TVsSAnZWagAAwpIQAJ9HMmEX0RVantMYzMvX\n9MNCgzLS5LDA3FsEztN3HvQQB0+RdXqsfQ1P9o7KPN6QfYG7bf03uS1rywR8\nqntHOG4KVAK8eWlQIie0qpI4Kxw3/Pf2AmSW8EncMOuBgTeuDZddi7MULF2n\nt9W0GYkNDFxGw0Oy2PZfp1aUPp77xaHYQVmUg/3+ijdeDrX3wqicOBQjrf8X\nJ5aZ4dsEbTmzRf80caWSQvdgVzROKyWljGIz13ev/27wfLba813PtPRN6v6c\nCiU9ptCMySuv07/5XxdMdxwHQrJDHvmB/AixTQD7v6CIHRIxf7EDYtLwKvb0\nYYRkVihS1CgHBJvFLOW8aehdYuCaCiDnazHIXvzQlLb74k6FHO8d/m+z1LXD\nQ5EsOe7U3+PVqO/y7+DqrDjxCKKsDPULU6oFum6TyVI/b+IOza8nE6niaHTH\nopXQcs80ZIucOpUj//3hNXDfVDDVcOZ0qGprsPb0nQiGsshQTIFLC7dcG3ca\nPu1VRNTCQlguYXmhGP3x7i3uDO+E1mkPV544TMeEe8TzU6NNSw7KSoWxtjMG\nXA1hxeJ+xp9ph4x9AunW/TjEeWNqLjHBMYc4OhxHc8tzgYGttasEoDFx1mTS\nh1r2GR3DKR6RJt/7fQ53/dTl7pWsA7ABcgG+iiFE840bkg5hl/D4N7JLy1n3\nwX2n\r\n=v5Ev\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFQa4wCHJpse0kdc3MuGOyNzYRGmIvahDrt5nz3lD2hbAiEA7VIpyeHBetlys/hbk/sVwCI5rKXBfR6ZVxhXp3Y8gJ4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.7.0-canary.74.26d827d71d0af7142af7cfecc7d2a44d385cdb15.0_1611568159032_0.34102017340669866"},"_hasShrinkwrap":false},"2.7.0-canary.74.c934e4283921c6cd31fa3d2a153b5d9f8f49bb0c.0":{"name":"@sberdevices/assistant-client","version":"2.7.0-canary.74.c934e4283921c6cd31fa3d2a153b5d9f8f49bb0c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c934e4283921c6cd31fa3d2a153b5d9f8f49bb0c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.7.0-canary.74.c934e4283921c6cd31fa3d2a153b5d9f8f49bb0c.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-wFlWnSoZn23Zk2I4LGvuuZxQRMCzXvkjcERAqe40gOJ71qgLQ+r0L3MVmJSy7luC/+CpmWuKqdR9ZgrVht59CQ==","shasum":"21aa88b081316e61835a9071e178ca62c186062d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.7.0-canary.74.c934e4283921c6cd31fa3d2a153b5d9f8f49bb0c.0.tgz","fileCount":69,"unpackedSize":1578917,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDs7sCRA9TVsSAnZWagAAJWsP/j+fApvP5cwknsRHP5Te\nSqpdzqiumpUPMNkPPBp27G+PxFiZpDPc07I7Vk8K/9x61QTmBD4X9OUvWoJR\ncxX9jfjv6ENiKsIvsP65Kt6aKXiXUDIZsvHrV03hDxFK761/FPuTFDNGI5Hr\nStzhp3u7VRiQjXgUS1J5TYWZVIq6MGrtevfEBUOnEF/3Vj/uBp8d/yePrSgy\npee8IT36v6RF329cr2/Y7vbfHP2Cq+DKxU8r3h26LizAYjTiV3Tof4VyLgoz\nJjGwXQJdTAGYnhVxSuWPr5D9+a80XXXm5IuOxnGMb6krb88zZ60MBgkptkDj\njiZxtChvrRMcQ6+f8qPaZ6hikFrxb/XFJwyXUZc4YOfW3NohfjPeoLTvj3Z2\nWCvVJpyl2xpNtB/gLf5zpR3C6dy93fYDBWExo+xJwGhwAp3aSuZsOH5SEvAF\nJIZ/u2iSxeyVbQlYHxaPGljaAtldFp1wjPE75syDUzvrU6ODNIBGQdG1Ft4N\nZMHzSH5UmcczJT4h6xRl26LMBxnFuYZP/zZsKwaVLzzFXeSJ4bQx9obbY9RD\nHJaUR6ULlVuP1mc1VHKT8mgbRuCPb4TxbcBnq9fPHZYYKygXxl/9sbLddWMk\nmZhzS418x8JONKrpsy9AU0cyW00j6E5Lq0iTWKJ6w/mOr0DiBC3ZiwDKSnZw\ns2rM\r\n=uZw0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDDK2mSO19OMIf3UHK2sLEnZm5cBY6GhhTOso6ywVlUiQIhAMxoi9+8KDiBik/wN5LOTEl47kIoyc8RMdEKfp6RNZmb"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.7.0-canary.74.c934e4283921c6cd31fa3d2a153b5d9f8f49bb0c.0_1611583211483_0.6039384864843274"},"_hasShrinkwrap":false},"2.7.0":{"name":"@sberdevices/assistant-client","version":"2.7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","jsnext:main":"umd/assistant.esm.js","scripts":{"build":"rm -rf dist tsconfig.tsbuildinfo && rollup -c && tsc -m ES2015 --outDir dist","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"94601382f7efea5e70144c3b3d6daf3a6de8dd55","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.7.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-QdLSXpk0ZR9fwFlkp9HGWTMczu2R9IS25XoGYF914m35G9bd3Uegrl31HXXiS04528aqlg0jiKwB7GsMQ7iPKg==","shasum":"d239de5f73f57089a4ea8c80b20e4a5ebd5b091c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.7.0.tgz","fileCount":69,"unpackedSize":1579359,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgD+uZCRA9TVsSAnZWagAAw90P/RfeRqhrrOxO0NJwpGf8\nWKCCjMbGfsueGLgnS7xgeh2Z5FjCevltmmTfI9PBnYfdl6+Ui0gGd13Iwc3o\n/I73c5Ila0UunQ/YI07hh46ngnNpE8fkfHmDVybYaj8fwvScELiMVnVWXDxG\nZtgHaSzQy1o8jBDBFtgj1yesPspmKsGoJ5sOVHyCpQFilc7UfFwUeU16QYo4\np2SkiV1uKyrn7GDO6+55S86viifYj7JurVVuv0sosHPVshXsLVxT0zsqAC0C\ntBWtppt/j+MRCVPVL/5u8CYusu0WiA62WyndkWwiHdiN+e9l9r0Fq+7pdBXd\n0rGcDmn/5YuBUfPF6jzFqEutivBPiESmxJj33qSVC+qP38b4Q+/ou7gFqPns\n1DXh1ORhPdh/1Ii+201thZbSy/39nT3jHgykU14Qrhc+3NeBm7IwxY9y55u9\nAYIyXn1LpCAPlMLzrKLybXNFXdSbHl4DP4+V6RaInZXfuO4oA+vmUxd7Ij7x\nvYXarb2kTBloRrBuDaLRUTqHBSwfynp0YOduSWUN8w1fIRygEB+1KGn87n6R\ngPqoCprMaLcl0tp9NQU0EsXLH5LQX+Ex6mH93sjfgKyl6uUmIaMLkl37XrRk\njuSvvj6fuiicRRDocv5Ch4mx/JkML5+rmaXYppZulzqQc2V0pqNF2rz52O6Z\nhwYT\r\n=Nyw2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICCGbs1ekT2yTB7UXiigylFQyjA4bOvPPz4nVULTlFsGAiABtP/7Ohl3aZn7Aoy2EwuFoGoeeywV6fmIQjbLrAQhGg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.7.0_1611656089127_0.20764280906648858"},"_hasShrinkwrap":false},"2.8.0-canary.76.082fb972d8eff6de1586abeb923a4f7190beabaa.0":{"name":"@sberdevices/assistant-client","version":"2.8.0-canary.76.082fb972d8eff6de1586abeb923a4f7190beabaa.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","scripts":{"build":"rm -rf dist && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"082fb972d8eff6de1586abeb923a4f7190beabaa","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.8.0-canary.76.082fb972d8eff6de1586abeb923a4f7190beabaa.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-3QjCq60pq/xZe3zhGd/+lp0OwAeKiOPYPRMIe5xRp0ZCF+dCR8X4qvHlYDpwffJag6YwRepdyrEYcZNGDPy0Zw==","shasum":"c9974d387da8996cf95e68809c488b9f8b8df028","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.8.0-canary.76.082fb972d8eff6de1586abeb923a4f7190beabaa.0.tgz","fileCount":43,"unpackedSize":805375,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgD/paCRA9TVsSAnZWagAAYoQP/jW7KHMMNL5js8t2s/Mw\npJWI9fY33DtF3K9gw6DM031kll87rePwsTXF+ol0aiEsB9GMPqTFn45gHBLN\nADJJdWEQmY+M2RaLC0Bs5lUJ6skfkGNTj9/Pqnqb/SE0tT24i76Y7vH4uFS/\n/5Jr/ptjhQuyusdBtO1svEp1Nz5qYvVOSaB+QgwbGW2Zcgf98IwfOceX8G7E\nGeOs4YaPU3wkLpbfOJmMlLsFcRXjc7c2wLTtkiL4AS7qWrfi+cCvqTQyzNfN\n2HisvsdEIGQFy23WiLU+U/HjU7a9rdiUSny3moDc4pZr+rcAO6T8WlFY4LDD\nlknscpEKHvrDFZ9+pCA4iunHRVZGcvYV0xDXG3AHju0+mrO40tbNMdLGKUjy\nnqJTKFn3E5lnc9wuqupH9QnWA4aNahZ5IQ8BQILAo4yKne0s3vclbvUk4wcD\nFDsPHYEIOjtmX6HdXEJqCBhFJ8/BYS23MaDMd0yaAfp38jTBtxyBJhUJMA+/\ndyAXrA+Y+WnG8FzST9CAG3tYSjM4RbfwvzN/6lSDmkwept+m3Q2bOFOrRIt/\nnkObw8F4smwHZ/FRl40lS0tUWGk/fM2ELs5UFvwZ0+TNywksP9FDHcuKl4Ci\n8+Ir/LnSZAhRV4jtFCaoxJ996P8eDiARB1SEVDPJDr71nqJSeK4Ee95jSEYW\nJxhW\r\n=dzJK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB6a+r/XsXvHqxlWh8fME6wqPFcTBuM/SwpQIgtr2xMAAiAmGtsqbGNyrWgNsBz8dPonx7Df/S7P08jQUVgffsDyjQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.8.0-canary.76.082fb972d8eff6de1586abeb923a4f7190beabaa.0_1611659865878_0.18591318599474937"},"_hasShrinkwrap":false},"2.8.0-canary.76.0f076a5f9c499380f3beea4be7174b87a6765a57.0":{"name":"@sberdevices/assistant-client","version":"2.8.0-canary.76.0f076a5f9c499380f3beea4be7174b87a6765a57.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0f076a5f9c499380f3beea4be7174b87a6765a57","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.8.0-canary.76.0f076a5f9c499380f3beea4be7174b87a6765a57.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-xrIjhuC+xIROqVhpjGVz+3w0pbubnlUbJsjzJixhATWSpTjOIF8BrJvkNQe9QScuzNdgJwAVblJbsWL459f2UQ==","shasum":"4da7f3fbabbfd6c752466e1954d9f35013d190c9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.8.0-canary.76.0f076a5f9c499380f3beea4be7174b87a6765a57.0.tgz","fileCount":44,"unpackedSize":1213682,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgEBypCRA9TVsSAnZWagAAOe8P/jKjteZq8gbv4A7f28xz\ncWq8oIO6XZ7FPNXKNL4LlmHGq0s+B5AxbqjRFu3939OiWYzNAt9F68oB0Lhn\nPbWI13o0z3Fh7UKrP5ybLiH6AofDD6DmTPU2CEF/bkop9Dxw3v0OHQnVV91w\nUF8yJAT6a21nDiPMEJ0VCNiCE7SGLmzI5Vw8Y8nHLjpMkqQOYFFF2EK3N4gw\nMNAzVDNAMzc4fzzt0ooYs9WVLB6gGfyJyvyFO097wXeN7e4uXHjcjgBm/pmX\nN1oXLJHjpRUVWlqq0HGzXFh4wZyXECSandQ1gLKYs4Vmk0VlAul+41m4cFsa\n/2SH3Qib4b00tAQN4Ny1ydQ7V7y2q8sitAZnSWdlWa2QLPMLnMWq0CcPunHt\n01tDCl5ho/sinljeehNSYZF8IlnNFARm9659oZ/B4KRnCO3WTpRTf6yBphEr\nLLG32uOPiUHWerdDj1C24TFuG7TM92+puY9+VXEyAfT4LMbLTqKy+MoDqgEw\nizo/W15HF3VFoUuweqLoQWm65rV6P8fQAP0O2dd17sVfapQdN51jtc+eD/FO\nLW5lzUObzuL0AxahFkQs7SelpQCemkHsXosr3rj9SgW6impxW3e3/DseBPCJ\nBoxX1iBg5klbCFxKoEJ3dYtCuNxsFLajGMtDEDRxIfRG5DTm+TQw7j3KAHpL\nYNu6\r\n=46jN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID9CUJ9Y1TzIeOZychexBYP8S0smc9P7jqL+EhzNma1NAiEA3IaJm5bBYwTXnPj0J+A06b/SC52kAoC5prYNeyItQGg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.8.0-canary.76.0f076a5f9c499380f3beea4be7174b87a6765a57.0_1611668648654_0.25689164410788035"},"_hasShrinkwrap":false},"2.8.0-canary.76.9955484cc76eed23c56d25be9b59366d9740cae3.0":{"name":"@sberdevices/assistant-client","version":"2.8.0-canary.76.9955484cc76eed23c56d25be9b59366d9740cae3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9955484cc76eed23c56d25be9b59366d9740cae3","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.8.0-canary.76.9955484cc76eed23c56d25be9b59366d9740cae3.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-GXhMvTtoZ57NdH7PrGWGoiMwo9/BbstPJqZ/g/5ST8AjE4c7WT1OtAxMk5ytFt4Ip/cNj15/0LIrxyzUAehgqw==","shasum":"4d60f0049ecf8b7e5176c40a258f03220b70cc50","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.8.0-canary.76.9955484cc76eed23c56d25be9b59366d9740cae3.0.tgz","fileCount":44,"unpackedSize":1030527,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgECGzCRA9TVsSAnZWagAAf04P/3oE6k53/5Q8qQgJ+EbF\nfyr5DRRoyqfHQ+VierLCYRtdie6+iw1SB1aJCjnZGJTLGRtw0t7KRix+6oof\n9NUAR+GxFvLyX1SMFXkgC64qaGzwG76knKJtWCLYZN0qIxpt9KFqVlDS1k8t\ny+meOHolekjhcvdbZaEt089sJCPlLQKJIRG+Ep/ydieRKHmm/2++kbLk57d/\nbbRfMO7C5WV3z33N7Pjlgbld+Zxrk9dMARYwb88zsN2IzOj8SZHy+1Hir1nK\nrFXuMzkX3Thp6mq+fnc3UKMVXoHEqIA4Yu8kya3IlkSwYQqeyfYitX5QHfAE\nJubmskb43RvVSErn/4JFhEWPgtrMIoW9XgR3jEW9z6gKP+U08Ibm1DoG0GRg\nYAn+XENtIzn91pKBcuH0XnBMyRwQyDTYvmz2usOsNIoMePSYkXRlp7Zlh8Vw\niXBWN6Nkfjgf0OnI2DWBQUa+nho55jdVwWFfJGJjNAzc8kMprH36+p5kbyOj\nnT7ZeLhd/i2Xnj1EndVvRjiM3xBtCj7bl/COmuh3MVV60WZDiDEsKKPq9+y7\nfHrGIEU6pS++mAZPtAamSCg7Ngj+h+uL2rCcHXM7n3hnrRPQbux3Apt2A7Ny\nrIWFXzyCS8N9wMfKxnX3U/bi+IfAfyDg/EhKzt9EXWz89ZmOyoJ6gHdMvenJ\nPtBx\r\n=AAkv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDjCnoxDVv6inruGWouRJzKbmqfQH0gMgEU+AT+ZE440wIgCaH629gXBMOOdeZh6mtUXRn5bGUMhqwfyheuUQzS5eI="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.8.0-canary.76.9955484cc76eed23c56d25be9b59366d9740cae3.0_1611669938878_0.5248540426089692"},"_hasShrinkwrap":false},"2.8.1-canary.76.6952513b0f21d83df371e50d35d87e0260ff8bdf.0":{"name":"@sberdevices/assistant-client","version":"2.8.1-canary.76.6952513b0f21d83df371e50d35d87e0260ff8bdf.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6952513b0f21d83df371e50d35d87e0260ff8bdf","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.8.1-canary.76.6952513b0f21d83df371e50d35d87e0260ff8bdf.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-feMhhbTF9OLQaq/XPJ0DUuT4WYesYDHsKTLH4p6A5X5hhLxfMJL0C2o2Yb9+X4Q0EhGs+KIwn70tBudyE5UukQ==","shasum":"3326d97673784aabd820dab70975b84bd83c49c5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.8.1-canary.76.6952513b0f21d83df371e50d35d87e0260ff8bdf.0.tgz","fileCount":44,"unpackedSize":1030527,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgECINCRA9TVsSAnZWagAApxIP/36LY8Q64cpv6t0hJuPT\n2dqPz03C6Whx9947FCvpT9nSeGsbxQ2PAr6CdbfIMtmu2iPvTD6SJQhABJdY\nM67GnMCTiTx7IQFwZbxgJ9LciKWboTsm8aehD9BG4qqniGX2fD0kwFFqZhrj\ng2ZY3N/3E6TudYoZbEDoNyuwMkAshnvHcFWa39wlEc7fnT6mHyQRGIRIFnlD\nHBMx0Q32laXKdigbmSveLBoz5EZDycV5xsDvsI/nKJoI0UAsXED05f05L3SU\nPwq7zd1CxIISVp6R6DPYzTdH+TxQuZyatyOvUZj/8cbiME3pnpSxAa4pVaj2\nvwwfU6gbUfEBajrL6zFH/ins2VNjzB9G29+gLcGDlhmw/O6Vmz8RE8WEC5lb\nWBni/P+2xqYc6txUAlnaqn0n5pEdezf59gNBE6l9rSEq9iAEzgJmD7gsIbdt\nxtXzGQl4spi39q/Is2uSiOgrxQFEFAHw0LrJnMFbivwK6WnZY2WoHgKO1dE0\nOxpRZ/EiyC2Ex99j9cFYpjEtJpCBtJPqxps5uA9s8kir6YK3mDSUoyTq6pg/\nf+Or31+wYjJBkI0GpUh/O4/rJek3fxIJNtGBF7AtjWRQQsxg/U3hEHRnNKBF\nrvNkXdZOlvO62B/nuTWp/Ab781H8/OBCLwtpJ6TClUoypTV1KwGdUwAgjxnk\nvqmX\r\n=qK6w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDIWII3PVk9Nn6iFMeM2V9s2OWrJvQz5mHVtvl2tgOmWAiBCDjVLFrpB6x1ns9/lj42hSiUB92286r0bDlVdRoCNJw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.8.1-canary.76.6952513b0f21d83df371e50d35d87e0260ff8bdf.0_1611670028608_0.24400883393738204"},"_hasShrinkwrap":false},"2.8.0-canary.76.1c7ffb9c6b52f5c942fe3ec269e48de28ba5872b.0":{"name":"@sberdevices/assistant-client","version":"2.8.0-canary.76.1c7ffb9c6b52f5c942fe3ec269e48de28ba5872b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1c7ffb9c6b52f5c942fe3ec269e48de28ba5872b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.8.0-canary.76.1c7ffb9c6b52f5c942fe3ec269e48de28ba5872b.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-0422KkaPnBPMaK5H7XGx0TbZsvMtk2aX+mvAEJIhDGsZiE6Rfu0E7c3m6sLg/qa0D5P15GcZoBU2+5TmCdzS0g==","shasum":"094cb31f0ed6faba1b15da64914714a8f54b5d38","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.8.0-canary.76.1c7ffb9c6b52f5c942fe3ec269e48de28ba5872b.0.tgz","fileCount":44,"unpackedSize":1030527,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgERRfCRA9TVsSAnZWagAAQe4QAJT7nmISnd5hGCZBjomT\nhQYA74VXxF/PSOtEqKZ6TfIXTKwpwUkU8SlaUX6SdRHZ6O7WyH91ADhEhsrs\nGNJz9Arb0aqUPxYZhRNyYw8BoSG78nJqoJWdZL5vkxCd2CdaVF+4Q3ajX2++\nneOa4F7z7hK7hlA4Oj3NflfhSuTy2tofraBCszSCEAIQEV1WmxtUbsYEyg63\n6O8hUHpLz3rjxCQKYMZPuHKlVFByYQ6TWDJvxkrulL1d79+3HSu1eutKRpev\nI8x0L5mBZNJ6lYJAP7AzsGfOwedgU03TV5SK6BAo6tviU0sdBZWZuSBEwAop\nMi5PduEbl6BcZv2VwQ5dK+kJIwoXImB3GcEjnfq7/uKKe1VpHVihMRx5zPdE\nIHG+5DlXbcZzQYAZxydRvxBqzN+LHCMqqGFi9VBYZ8dgjreM7AfnDN3/3ZZm\nIAypDP2G1ZrD1+0kp94ud+ESzl5UbUhIDpDP93POD1ntsHM8agKBxZUg8Lkh\n+rGUp2k7kqIwmReIOp9HfWNvomvVEpymRTTQHhib3rscuGm377C7VDjqRxNM\n89rr9dPbYmiTHC+cwIZhr5osRAj8SusCx01Tc/mj8zrHYeCDF8U9gNkepNaI\nWqUjfcPVOpC7mCNeAbLCuySynNgHN59wF1X1eYccHeFCI5pRY3qhJADOG5qh\nYpIG\r\n=wWRv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBbO54XbapPXUJjg4B+vtu7/YtXwP7rWMi4AAgpBuD71AiEAmtnNzb9q9UKZKbkDhwQV3QpvYfxIhtNNK+kC/YgmdJU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.8.0-canary.76.1c7ffb9c6b52f5c942fe3ec269e48de28ba5872b.0_1611732062455_0.0027748561150311435"},"_hasShrinkwrap":false},"2.8.0":{"name":"@sberdevices/assistant-client","version":"2.8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto.proto > src/proto.js && pbts src/proto.js -o src/proto.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"841f44f293ca0bf8855b866352905f5bad93f2ee","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.8.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-c4pZH1mJIGh0VtSg+tpnRUE91gpaqG0t5dr23N8t/V6rUNBz+z/DhVgh3aXqkVKJlEV4kFoQvTZi8E+fxif2VQ==","shasum":"0e6a94e895d253a1a5699a390b0e6b8f4a741840","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.8.0.tgz","fileCount":44,"unpackedSize":1030702,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgERUnCRA9TVsSAnZWagAAtdgP/3p233xyePr+ghDh+7Ey\nN8dSXQYUV4x3INhEu3voM3wFjRFfArzPQPu3QbHKdOoD8dNsuhrULxNrWFMy\ngogppEJsi35KMYwHVoIJacPc9XPVPwJSFr7e5EGopRWnGaZNHzwtGZFd/jpT\nlZ0fiVyASOFugqWA1KmjPbCJC5Ydz0Sks+nw9efktibNxzQUxo18MKPzZxMB\nCO9pOuCJqOcszB2wtO5Nh6oe9DbMGoaR0cM3RKUlo/O7L7M83qywTmWgBI4I\n3Il7lF3+yEol+NmfefniHnssNZgvpv2NXxkCrStwq5nsuPhDdLvBx5k8kDt+\nsJG4Cx5LpVAcHp0LxAnIUByJm94h2DzQjkLv+9pTIlKVM9CkEWeENmehacoF\neXu3u8Y22H+vIr83HUWPb2JlhCACtmVDmHdgD8WiYKEZU7sMg769cZQ0ASna\no79RnaOMGRHhkGn4gY5zuJK6jhD65xXmkujTdMRUn4u/TF+NjnJTQiSkYhS4\nVsA2sbA2/MvLIe2JRVPMyg1u8eQMLMSX+C6t2buxuvEJMOB01e+A2w7I9H3j\nyhPD6/4HZYQgsIrSJMFkMQx6UqydsJUcY12Tde766dsnBt4KPQjokEcQByBn\nma+jOOOWIBp/uCLRw4avB/fgr7S93/o0rMUDuAXMskZjZVBZzDhas9YCnMs4\n9TJf\r\n=KC+A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICPyk4JJ/ancbd5md/Dz0JKOsjsL2Y4uYP9vvwia/Hp7AiAWwolrtmSVxV0QdayCbpOoERp1wJv0ee0k85lvKddCyw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.8.0_1611732262944_0.7267534794013746"},"_hasShrinkwrap":false},"2.9.0-canary.77.3ac7741230d224bdfb6a2e2e0c5152b09f7d9a1e.0":{"name":"@sberdevices/assistant-client","version":"2.9.0-canary.77.3ac7741230d224bdfb6a2e2e0c5152b09f7d9a1e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3ac7741230d224bdfb6a2e2e0c5152b09f7d9a1e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.0-canary.77.3ac7741230d224bdfb6a2e2e0c5152b09f7d9a1e.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-L3oheZwLOfR8EZkRGu16hh+MwkfzYfNb30hTTGVDXgl18mMmNTh8qDIL066UwNb8pKtjAO6gEZlXS4qpxpQBaA==","shasum":"b81db2428b303d6bfd8c3d8d570f7b22a250c18c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.0-canary.77.3ac7741230d224bdfb6a2e2e0c5152b09f7d9a1e.0.tgz","fileCount":48,"unpackedSize":1174450,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgEV9QCRA9TVsSAnZWagAAs2AP/jbotr+rJjHh2ivzOser\nHoAni7Qq8DHoFX8/yPdZ4vWDPK0IA98lest6vO7DYm8H98Ug/yJcRsa7fSYb\n/tJa8G+iAv0YjrsTELdwu2VXyGTfi2lGJyq4gzrUedLhhCy2s/xje8WhBAQH\nmMxpnd7yXF+Sl1IK62iC5+f7Q9iZJyRGWMZtVFNkOUwG70sHZ/d57LBQ+xVD\nju5h/GYvSGmk0kAYtWYBqwaWgl42xEvBLuCNQxvJkmUW9BCBn81uNg/S4qVz\ngCwvOADC+d6VlyKyG4sYb6+xmcq8ZlhVC5wAk+r3+4IcqEtUVpstjiB9ubRV\nMvKD37WRWV0SlpwahQFQNHsP6t1evW92I5QwrFJbBu6pWCzG66cHPVamTlbc\n/V32DYuMvludJ+iu3sMw0dwpm+Ywo8BDA71kgGuUad2v5aDG9CdZUuIWKpr4\nME8iJWeXuq3k8Q6LYiriiGf2PoP/I4b6whk4MSpG49+ehbtYyZoy8rwifTdt\nsLesx+x2HKQUAmlPf6OCZIQpxu20C3xuKoWDqeHUNZUEN0Qe5BFc8yfgriGe\nbbUEwT55htRcm9ZdlEGuYJfm+KBhIXFniDXLReWdoVDWZPKMmKXL7W5uCKbm\nqgqxbf4Tw88KPUq/al5dXLAcEuQiSMN6jriAE0/qELm29cbgKa1SR3GDsJQk\ncqqC\r\n=Tp5N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD5VaDHIlhsyKFD/S1/u8oz8DPLXwnrw8HO4kQm//caLwIgEUogZY/5MdIKiahsAqPi/58YW/XK4fCsSFFBtw5HbAE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.0-canary.77.3ac7741230d224bdfb6a2e2e0c5152b09f7d9a1e.0_1611751247928_0.2338815944788486"},"_hasShrinkwrap":false},"2.9.0-canary.77.a5d2cab213e3d0e51f9414659158c527c1f4ac15.0":{"name":"@sberdevices/assistant-client","version":"2.9.0-canary.77.a5d2cab213e3d0e51f9414659158c527c1f4ac15.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a5d2cab213e3d0e51f9414659158c527c1f4ac15","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.0-canary.77.a5d2cab213e3d0e51f9414659158c527c1f4ac15.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-5vO+QMTAB46G5gcJVVPf7q23wtLFqoDi2z2rTaC5jSRDL281uASfvk4pxsHzftZ7oSKNU5u5wNcbrHNfuP+qvw==","shasum":"60e20258007a161fedd7283b77e88761fadd55d5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.0-canary.77.a5d2cab213e3d0e51f9414659158c527c1f4ac15.0.tgz","fileCount":48,"unpackedSize":1175264,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgEWZbCRA9TVsSAnZWagAARqIP+QEPqrgZpPuBXNIQSQJX\nxJ/76jf7Cl7nrYy8pEI8NtckI6XCVtt9x/hKNMwhYXhIfN+mBSrwJ3TFyPeY\naLtXWLgtJcph+oYq70a0sIL9l4UUUPx+P7ovnSQvXYmQQS9OHlLBhVkLMHIW\nLRmBxuHyqJzr5e/mweteCUA1cdRjo6dl3JRuEYWz7RrzE4d4EL3UznkhQdIC\nqEmtAKei3+Ue5/m70TSclVzZy/BVJcIBcpp9dd8c78uPuMRui7kbqAh74puG\nXrlT6s118TYlOCOOj7tboNdZCkjnHMX4SbIM1r5dMMfPPRcBorx8hQftkq/k\nWAXZ3/CU1RpuD42irZIZ0x8Sn0k/Tq2HTjfVMD8HPFtTX8DIZjcbIT8OKtmx\ngog9O+kM7b3tpdXCZJsFh+8cgeFnZlwimoRwj4uLTjdyFCrJsIhzRnKtR3z7\nQN3rm+oths9Te3fWEwqnwP03QaKakyLR4I2khHU1jHb2xpdei6z7rVQgfEHA\nF6n9z2R+UaSsDpU7G/Y6KAJ7Ri4ytS4gkqlYU37cTjBYJbxshmZ7iDdgaqm3\nndIe+uVAQtZsbyQWmFFriN0nOQo2TfErZrHzV4qCmjD5GfevRB5TuJ88mwOr\n0L/4eZcyoXlgdiovmn5e7oU5zMjvh0bOok06nCfxqCcb7JhWX4bP8rYYX9U3\nhp7N\r\n=C4fj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCSLXFKN+wkKnBrS3uNOkFtsG5O2MG2ilErlxUICuC4FgIgby6G905TCE9EYKNPt6dHHSCok0MQNh4g3oAYbLGo4/M="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.0-canary.77.a5d2cab213e3d0e51f9414659158c527c1f4ac15.0_1611753050836_0.2904012255402064"},"_hasShrinkwrap":false},"2.9.0-canary.77.3d9e13e0b3feca4b6e409b8f1ea3731b3b13a494.0":{"name":"@sberdevices/assistant-client","version":"2.9.0-canary.77.3d9e13e0b3feca4b6e409b8f1ea3731b3b13a494.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3d9e13e0b3feca4b6e409b8f1ea3731b3b13a494","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.0-canary.77.3d9e13e0b3feca4b6e409b8f1ea3731b3b13a494.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-X1ID4d0Tb3vtgnrroFNy7/fb4ECdE2RkGercZnxH1A4zQ2bS9jq1PyJzGuk8IUECF+geWNGHErROSIpHhp+I3A==","shasum":"1c75b22d5035602206985a46108cf54d7d882ede","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.0-canary.77.3d9e13e0b3feca4b6e409b8f1ea3731b3b13a494.0.tgz","fileCount":48,"unpackedSize":1076839,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgEmfuCRA9TVsSAnZWagAAnmAP/3SkWqjSWdSrUJVmGqE+\nn0QaqPugqVPJBiORBXxsyM7FQSFD73uds3Gyuy/ImrbTW4yd+LrjC1Hj43Ql\nswBKrYcjE1ZW9GpJQ1tub21gPYaXGGeQw3J+J0D/2/t9hbRc2wvCpUOqSMms\n7xmBpy/SbD6Udb+LnzC6Ngl2X+v2Xub8BdiEbbkW1xOnoZ+Ad+G7cR+/NH2T\nVC0dGe4P4T5UPtBdngArjHkQ6bKX/oRXutrrZLpVhZLuOobRYUP2iKyan5Pt\ng1oQ2tDn3iZ0MFbyHzRLm/sHwhfdaEsvamxv9OgKgulqWJioWChvLDrtKpMT\nvdOwByLVhqjZkt2M+bmT8WPB8rX6H3INX40cOm+Yc/LQYbwMtFRZONcvY6qx\n8Ut4dUrtCRoziYgqjsry4ZQkLjhwjrpllAjEjxzfz8qR9LKbKiUMMa/nyJR0\ngFRigV4F/PJ4oEAQ3XZZzYeLOGlCQGa3sdjjVaAA4CTS2hPH2QIDlUtI2FsY\nbhXUu7Vi85CaSHJVMXVMQrVAUVVvWYsvM/M9QI/c37ITK88Ka0CeHBRWsc2Z\nQ0ehfjnX5DVwlGrCNvuaHAKI3k4uygReS8xYL2QCwTeOw38cBmCPBIN6FeqV\ns/N3Qr3S84BBILzq3zUiYl3PV5G2dMa++eNrV+yCP2hqnwTbdfeiO2gC12pG\nMJBp\r\n=7MZX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGFRDCHOhX8ewzSvrXot9NQI95opqe53Q/x01t3EsOYRAiAiOcfcBnwTIikGISTLGGufUkPUQlqOVQeMNRnhssu1Ng=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.0-canary.77.3d9e13e0b3feca4b6e409b8f1ea3731b3b13a494.0_1611818989947_0.5158409522845311"},"_hasShrinkwrap":false},"2.9.0-canary.77.344e6c32e0927897556df893cdc3092721a22f4d.0":{"name":"@sberdevices/assistant-client","version":"2.9.0-canary.77.344e6c32e0927897556df893cdc3092721a22f4d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"344e6c32e0927897556df893cdc3092721a22f4d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.0-canary.77.344e6c32e0927897556df893cdc3092721a22f4d.0","_nodeVersion":"12.20.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-dNIAozUFjLxQW5qgI1la/BO+j0a/mOtnzOKI/J3ZvwEA2ywaUbvNuFQ5+kRI91xzngnTakc526mIHsGlsZ7MKw==","shasum":"3ec5aecc8d837f8d1f77915471df29cbec894df4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.0-canary.77.344e6c32e0927897556df893cdc3092721a22f4d.0.tgz","fileCount":48,"unpackedSize":875285,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgEmosCRA9TVsSAnZWagAAbQ8QAI2xd8xLo25VPfLB9H6p\nWTmawmxsHAExp9vKSHWy4g97wrcvWuVwmc8uzlfZYBuK7U4FSq5csRG+U1/9\nBwQlKGD6oMecBskPI5VM/KhKxtIBKeDa4arJIiMiBMOv5Q/1Y1FnBSuKgHzI\nQet3Ak9lLQW0J40HDZ988Wd+tK1JCTPkTcKHVE7nng08ei5xyI9I0OL/dHyW\n4cUhxZDutRHVVk2jkDVUok2HXtpse08qpgHNefCcRkAe4pfyXXBKd/HN0MT2\nA02CNPY5NC+1D/qhgyweYWrAZMeG0flhBRQfwChhFV1cCH2YI6ASb0gyVT4M\n9VXFOZSer8WyfT/NshHUuwuS3jRLP2JeHP6lIDBjatAffi0FmPcMuUAsnAOG\nGdunmArqIlqwBbf83M1UGyf/bafiIljzFm7rZVhhecG5nUYRh/MgqSu7oCDU\ndvbf8XIlXA6zRTKWIIhjwXRLkCZgl+vHoa8pCFDnGyq1PBvCvKvWM3Chb9CJ\nXvYsvgMTQsDZAcN62CMN9jnexaGoLc+meyUL7BVIAE4NXZgokl30a9vb7XvH\nrKyU+kewtryxMtsBuc7xBS6N+LPJ2A69zXXLfXdmmFHVmNppoLT8v/UjNtG6\nP9eGMqWhVdH4Lz9VH759HunIFQ/XfqELlbA+XtNqOEjuaFW5ZJZz9Kja2RBt\nzeAV\r\n=sGdd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDbwfVeDZBc/7146v1A57O8xABxgdUgEzoLIfxd3LdpSAiAOJg27i8WHTYoFXlor6IQiv6CjMo/+7vX9iy4IYLUQ3g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.0-canary.77.344e6c32e0927897556df893cdc3092721a22f4d.0_1611819563967_0.6903378958023145"},"_hasShrinkwrap":false},"2.9.0":{"name":"@sberdevices/assistant-client","version":"2.9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1f30e226b14306fd44ac77149c38dddee6785ab7","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-XWcVRpsip7yJTAcx+WaBDKB6T2BdMUOD2ME6v+bteqN0Cqarq8F86LLVCfosC5wP7AFWtdZVgYMMtYik72SMKA==","shasum":"f41bd72364f640c2d19f9d9d0c8856b0a297c90d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.0.tgz","fileCount":48,"unpackedSize":875601,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgEpW3CRA9TVsSAnZWagAAvGEP/1eIdM0d8oWbtAXksRoC\nsHO9X4RcKum5hMa/zwoSjUfk/7RB0upl/bNogJvlIyhjHF6Ll7rNw14Pvv5J\nNp77LiN7q1iGAXLc/jp28z1IVD4kjoWbq7hk/OIcSeztv1entl7aoDXNy1FH\nV8TpATzpkFtECMC20Dz/uxXY1hVCnqJK4gw1IoLKOVUYP0GLu2hdxDbEfXQ3\nTw/wk1iJJYDxLPX0YIc13f2sSjOvyTxzjt/t9ygpqEyLUBuqp0ty4Nxkn8EY\nt9uWhVfzbyFT9yyf0WVoYRmO8J8ZbMbp9+/+jT+i8Xw4JxzmYNiXaWkEqpJk\nbwKssLbNRpM7MZ3+zHdsxHQqWUoGQQFwq8XKjWOTsyCTf9HAIczE3XuRJFEh\njwQGc2+N4nVExwsWnZG1xqZwn2gk21fqhjj6WmSRovFBdB63ubMM+lhwyVt6\nYVwgtlw6Vaez0c/Z/4zVY585o31NFj0E+QyNIYXpCk237xZB3dPySvn7nkbf\nqRN7cJCQ2UWXRZOgfHyUgvuWZk2xC3lMSWVyzognWHUrQcXhPQ01Jk90QxNF\nZP3WcjdGoHSS7WvSAFCcmY2lHUDwQ88LBr50UJuHKdkcHxd//FA3L/HGEQS/\n9oaTnDd2Bf4VsVL1OBO0Sk9WCyo4quraJXJ7wj/xpXVNApWmRFQKSAT3qc0i\n7vX4\r\n=zzYp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHBgn4p4BJhTev9yBWKEUOYzetxM47O2EW+EMo2TpSTgAiEA84SI3bc2je4WGy4K94Tsh8/fkj07H0n+MkbeowEAT2U="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.0_1611830711293_0.203114349510797"},"_hasShrinkwrap":false},"2.9.1-canary.78.055d91baed3d32f1de0e9070e72f39f56536c38e.0":{"name":"@sberdevices/assistant-client","version":"2.9.1-canary.78.055d91baed3d32f1de0e9070e72f39f56536c38e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"055d91baed3d32f1de0e9070e72f39f56536c38e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.1-canary.78.055d91baed3d32f1de0e9070e72f39f56536c38e.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-vvLEwQewa+Z7wXNlBRvQblsy5nA/fRgynkkntstNQDP9PKJ8s4di4VWJ7aP5sfojBaDAqUHb9zqJepF6M2/D8g==","shasum":"2c74d9d5e3b7a35185449095b1a945520609efd1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.1-canary.78.055d91baed3d32f1de0e9070e72f39f56536c38e.0.tgz","fileCount":48,"unpackedSize":881027,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgE7wdCRA9TVsSAnZWagAA1j4P/3LkRucsWT08kYSQeXaq\nKOwGh26B1/B7RlS9hD00RT9KgqeWDNRS25Swzl8lpY3eHa9RYUc3p1Z7//OX\n/LcfaSUk+6aUH1eOm+gqow/PopKbs8EI+jUE1+rL+0I7OkZUIM8CNUY1z6W7\nFR9/pHvWEcT+ymY69MJRT/vNpQfgyvgtiMnWNeEv9TMXpNv5vA4tzQ/3cVvl\n3Z3w6tj3EFjllDbl2l9jOQmUmBS2qflb9jeVfhY+fxfVUuv4KFM6S2sqOpSF\n0yrLpf11o2mwURhF1uvXyjWHz2GEZksM4lmi+P2+2WZXAwj+/JCjoRvgDNgq\nRYjs83zuzh07eThTX2q7yb47XIn8Il0Ri+YKMZW3c0EqmbyXVFVqUqbX0EpG\nhVaSS3YjcQ/zxouta0zZZDwZTa52jd1knQ4m3ZD88+Mu1cnMUJVh6SsiYhlM\nQKoeQWTlboxSc07VxK05M9RGtPRBV4aDtUmZGAlVG0r6wUmLQMQeKwgJXZok\nFe+0+wLNMKhaVT8vwBDZUPZittb+v9nUuzSDpYKVSldiLx5+gBOZHG/4HIKn\nUvVo6X31L68WixQHU6ObBGWP3ebHDwDPfhCoy/SuIyjL/jWK83a3CZFDbwpT\nSaFeOEKII2baSeBbXWasJ7L44r5SSUA0uqvFXKdLRTVwAwmaery8ci2XPRGd\n2Lw9\r\n=GF9P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEVI0X5yEJ1pdR7LWfrpj6qvBWI167G5G4pDEXf+pcvCAiEA29nHnY/4gaMBG6reYNq5AVfuNZJ6/kmPNcYp1PRlt2Q="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.1-canary.78.055d91baed3d32f1de0e9070e72f39f56536c38e.0_1611906076638_0.4889711715819651"},"_hasShrinkwrap":false},"2.9.1-canary.78.554376f5cd657df6ad718e8f75efc7b5ae8dbb72.0":{"name":"@sberdevices/assistant-client","version":"2.9.1-canary.78.554376f5cd657df6ad718e8f75efc7b5ae8dbb72.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"554376f5cd657df6ad718e8f75efc7b5ae8dbb72","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.1-canary.78.554376f5cd657df6ad718e8f75efc7b5ae8dbb72.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-eIw8OyAutmSk3mneFHOWNxy8/FNbmNu/SZ6y8aiJvL/Qr54RAaqHbAK1uP+1QyKH8tebl5zAAtQEoWF3V0Y61A==","shasum":"1d94326262e866b5926d23a7d6b9eef9e0123297","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.1-canary.78.554376f5cd657df6ad718e8f75efc7b5ae8dbb72.0.tgz","fileCount":48,"unpackedSize":881027,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgE8HQCRA9TVsSAnZWagAAtGcP/3xgYu9KJRFN0I12Ds5o\ncDXg4LW2niKBzsxoV8foAs49QCCvbu3lY/bdD4jWR0wJQDRjrkidnbc/CKdO\naAYvZMqNWJhwJ+rnDdTpDAPGA0thbaeY9HEDsAl7iEjeviSb4VFwtP7eMJ8q\nUBzSYi8vSUYUyqnZ5kLk0eSZszHkH+iLITQ8lEghB4SVaILMKm5OmuydxO5X\nyef445PZRRW2n5cgK/I8AwfTmCr3l1TV/JYjB+3ilsM71ijuaWgJeK7MnpPB\nO2cf4cHrxh9PcswEM83sDSYqdLIgvXV/kzcrB8Vr/P6c2W4J7/iqBEKZFuuQ\nzaxqm8zypjNs9qGILFeXAqAOqEuDJY3vtDD79bY8jKkWD8c1KG3GVobIOvzh\n3Fn7DIS2wgdNLksIWzkqB1yfcYGmAZW025JHTOA5vcRXbGOlRirzZVgyt/41\nnkmNOb+UNtbUEWxF9oOAqpNVw+zsh7LetzeeekRceR8ozBnlTS8q13ZlTSnN\nwvs+XG113cbDiqS+lprElHPXaGhG+SHR5AZYdAGKXDzEM5mJviD3vLF3hPVL\nZ7PxpvMgBJKfbhY1x0yXE8tHwfi7GjirMkGgW+bLq/UfftfUOVbMiCS2FrlO\nRQ2uplRvgtbfGmQeydiutsFJL5re9TknUAxUJDcSWpkhJ0Mds2DIlpdh6iq5\n11IZ\r\n=aPCY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFq9tdlbRCt64Ih+9WhFkRCfev/Tt0uPr0Nk+gQSM0+EAiEA4zULBgnyT0V/VW3KG3NQHFIpP5G0fRG3ds1VgQBT8I4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.1-canary.78.554376f5cd657df6ad718e8f75efc7b5ae8dbb72.0_1611907535999_0.43342146502214574"},"_hasShrinkwrap":false},"2.9.1":{"name":"@sberdevices/assistant-client","version":"2.9.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5c1660f4b6afed5d4d9e8291d2405c97d7f50409","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.1","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-UMggAQAK4pUNe3IRZ/Ei3wFv1JNrQli/EXKZOv33pxJ4fe69QBJ+WMVnT3gAB0HiIt6bD4mEuFhpUIiISRFnPw==","shasum":"fa5701ac1e542fc09bedf9860eae88995e5f25ea","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.1.tgz","fileCount":48,"unpackedSize":881142,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgE8USCRA9TVsSAnZWagAATEMP/A1eN1HA86KrnRxxX5n1\nghyL4vhbJIdxzgbfqPcLAZVtFznIl/YDZeSdsjqtdMtr+WOLyYaAv0WMWBF9\n1JO2t1Abvku3xlKH6zoBxvE6BmS2/LyxbJ/vPieYfVjNQLLMilY9Yi91dWVS\nk3MM62PfCyTmJPyNgTiNahd9sQ8weCfT1/kO8DJnHt8b+b4Gw4JGQNitqz5U\n857Ra4YOLVTG9IiSjylyNClHN/1oydbdDr9HSUCVPU/rqlTDTbji67JllxEX\nHd3wIeKhMb7b0jHm2JmbmWJw8KBdNa4/YWiNDLdnXGPnuMTgDxxiaS16UUPG\nKS49EvyvLT2jCIoVP2O6/5BEBxvrjv9Zi54KewzA7d98ZdfKCszgVAbmpKI/\nHyBcMO3KZ6KsMhvdeQ+gpunMguZ/pFmaWUBqylmaGeyHOn+ty72riEhogCI5\nr0SEJjcj7VkE2PYajPBBWk89fG+bNhMjsSMdpLFJhdKeV1sotEMoSwethEGG\nVB90B5UoVdyPwylcduJxe1DZDJ551E2Do/CutDF+CIQmmc3HJ91H4Y5wScNI\nAKjHWg96klXGeZRYJFL3YbAqcwwslo9Az9P6RyxayxzTRrwOsyLXQmbuXTfx\nDHpFT2J7lHiz+UF7JycJVrx3cLt3AR/CRBN7oGHXtBrvjd45k4boi854cg4U\nq5nA\r\n=vWiF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICpMBiInaW23pWIRBR5P4L5TxJ97OUWmbgXOaLA9f8bCAiEAsF2rHdS55tVU2CJv/cdQ9rq8E42pRjujyb1Vnm62ERA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.1_1611908370028_0.2713333662627144"},"_hasShrinkwrap":false},"2.10.0-canary.79.77c24af4d9fbcedca207a15506f8392b298f191a.0":{"name":"@sberdevices/assistant-client","version":"2.10.0-canary.79.77c24af4d9fbcedca207a15506f8392b298f191a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"77c24af4d9fbcedca207a15506f8392b298f191a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### sendDataObservable({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента, возвращает observable с возможностью подписаться/отписаться от результата. В on('data') ответ не прийдет.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.0-canary.79.77c24af4d9fbcedca207a15506f8392b298f191a.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-iojFJmLTvWg2ePHcoEgpERSOBj5vtG2DcjGoIq+aHmfpJcUO3JRx4Io3nqYIo+ZSxScAWEh5wJj/fnRukydQXQ==","shasum":"c1a318cb5c7b4e2a55159d79ff529a7f4ed96a94","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.0-canary.79.77c24af4d9fbcedca207a15506f8392b298f191a.0.tgz","fileCount":50,"unpackedSize":885981,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgFAycCRA9TVsSAnZWagAABzIP+wZcbDCrWJL7UeTpYXfd\nHQpyMrB9cSRXEJIhARTm1EV06NqChpa2vT1UGu4EDeZJXyLbO+0AGVJUBHbZ\nS9m8qw1aNIc2wxb8gmWuYk+FdvJ1Jy8sn9gBytYOOu+CtjMPOYy4XKMBbmWt\n2AZnJmymRt86wnqJi0KJ40tNsjgS7ytcKpTOD8RUrw+GCN+aN1D7SP1/lSFl\n721cXhaRHKE1oTcWFp7tMC87kwGMe5DBFso3m82Aqa9sA7x+Kpn/sn/h7q8J\nyP5lWsEuMA/tS54GQkBrFXcP6Zu1dr/R55ACWviGO1zw14UbjZFL/8U23eNn\n8Gda7SR2fdXCxu9P2+3EP8OhhO37PiczDOneBxbQku8C7U1Wp4Xt/htL3vHk\n7/zfpI5ZxCza7L884WX8Hg+d5r43T7t28uHzFgW7ma9mNJ81iCMNbUXm2bkD\nZLS2I4v+nw9E2nzeypBDDA3K3P4PentglYUfxuz6vomVO8oBKbeCWtdrcVbx\nbHWNcDJezV7xzYWmNHX2PI/HKq/K/C1HjltqnVUP9eseOkK5ZmZCSdGPRle+\nP8F1cR5JBg6azvt1uB6XnyISk8aJejRQyEYhk28TZ7v33NiVRVb3+S9TDmQ9\nvlPzzgj+OEQPqxZ40javkUtLcLc7Pcyj/hGHfgxdvL445YLafBH/wkBlQ3XO\nSal/\r\n=Om1Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID1TKFxoBT1ngrcft4kfgz7lqJpov+Z78CSXxVOpSMaxAiEAzQBv8+hOr7YAG8MEG59xk32cjXRllk+bhRVzxDQg9q4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.0-canary.79.77c24af4d9fbcedca207a15506f8392b298f191a.0_1611926683644_0.7775057368824656"},"_hasShrinkwrap":false},"2.10.0-canary.79.9eeab22bfb9e0c0c7e42e29bb5559ac44625f2c5.0":{"name":"@sberdevices/assistant-client","version":"2.10.0-canary.79.9eeab22bfb9e0c0c7e42e29bb5559ac44625f2c5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.19.2","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9eeab22bfb9e0c0c7e42e29bb5559ac44625f2c5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.0-canary.79.9eeab22bfb9e0c0c7e42e29bb5559ac44625f2c5.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-a201bqT0Em6SlzuvOOAqAU5eMUcAP51fYbiX5VoNRJM928qWT0jcs8p+dtVLQ6yx6DKif86D+6YscAIh92c92g==","shasum":"cf94754ee3bbf1561d4c6cadf4db7cb2433bde8b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.0-canary.79.9eeab22bfb9e0c0c7e42e29bb5559ac44625f2c5.0.tgz","fileCount":50,"unpackedSize":886584,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIPeYCRA9TVsSAnZWagAAMOMQAKDe9IQbmQALcbTV4j7a\ne5qoQM0cXaKZoxeCnpmRakQ8fb53mWQRSRVd4CDYkxjwiLj40c+RBkJcw3Ed\n1X7JBkvdT9VwjaEe0SMdPqw1sWQTjOGxCtQncqC5gnAXuzI1hGJoEetyP6FV\nNeLivN4tJyBrHMDbUeJJlQB3bjlT6DRLqP0Ls0aQ1TnoDzsNeOFFkYyQfaIl\n353xfwQarNf0qlQYRqrqfsuKvundd3n6SQ2LNcpDCi/ZD+rxLfgusj9RA/Dt\n3qyU/uqi4pGSRH6gAJiCVQHa6Hf2VQ/S6IFan37UnHkavxUmVSWURflNB6pf\n2lU4Z5nCfa+0NsdbRbjr9dANVLyvtinEzZMbHfsAGejPVcIqKPaiS3+GJwBy\nGu8IR+mjrlAV9o87Y+bDWn40zBXg7/8eCIzl9YE4MqxOQPUPZ38H4+6kxxmc\nK2pL/uUZNFHhVlwJBOF9D2Kuqyz4JJ6JiTuAYfDMdLlolyHP1lLsQ25bf4Fv\nzt8ZVd1l+0MV0Da2gZeyhWmK5NG4oq5eeriKREY49zrxBeJuUCVQmbkS0I5y\noL5m6/xgy8G/X8F48K1pVuj2a83dbqXnVysMhKQdwWeTFuwJLcZxCaRjSTIU\na+8C8LNikk92FSYd+TY/jUox5UGq+3bQGKSwei9qP/3RDqPHfXYyNKGMpysE\nid+h\r\n=1V0n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGK+8PGHINLbuxdkHca2HMwx9p2pnP6T+TB1z6xR4kqnAiB90eFFJMuxhuH4V+8h4oj9kVym08Bx6uyRTsw2uSNEAw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.0-canary.79.9eeab22bfb9e0c0c7e42e29bb5559ac44625f2c5.0_1612773271970_0.24497861326459014"},"_hasShrinkwrap":false},"2.9.2-canary.83.4d2d73245d2244318efa37864b47325b19935288.0":{"name":"@sberdevices/assistant-client","version":"2.9.2-canary.83.4d2d73245d2244318efa37864b47325b19935288.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4d2d73245d2244318efa37864b47325b19935288","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.2-canary.83.4d2d73245d2244318efa37864b47325b19935288.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-Orr/pBU0UrElYzy9X6I81qlnSx/nNSXpVf16f1SVHOIuD9jGHR1Kr8uYns//Wo3IY+TmamBPKOySufzXmEMPlw==","shasum":"f84bf2b92949d9481bb6830f073efe23326e1640","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.2-canary.83.4d2d73245d2244318efa37864b47325b19935288.0.tgz","fileCount":48,"unpackedSize":883383,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgKka0CRA9TVsSAnZWagAAOToQAJMYI0Wb5dnEFDrtijLi\nxXctH2i+ctxGLu0FXJjqEKhm+JYMlv1+NF+CsMpDUb4ZvmNnCkb8wfFsXMe2\nzxHopnRd2Dy4TuBEtQC3MwVZnskH3xHYSYJD/niDQ+2JlQNBq4C74FXiVDNx\nawBlsH9qyY6hNHJQJ/o9KWUomDPzpBPQZL330bEzF42UwvlwI4pFh9D5AR+q\nDTUzzZpTZwFAqyoX9z97ortFDlkQzyv968SwRSddgkpfuXEbqBPM5z6nGHQe\nVhfpZdToiKcnbRk1l5Ii1hc4Lx+nDC6rSvfUGTGZ8G3GA2TCI7RIyG0/nNRP\nfWKV1uIR1sM16PugKFDXClcN82eVNol0oGT4xOoFvBYqMBlYwBeymAPt27B7\nsbgRmQZuK1rkhnAgf0fBxMdc5y/kXkMGnR5X1i2NRBLVin015EaZLNdCLOzW\nYyd6Zs3d/kNxhC6uQoUWY9nlJ6MkE/GAhnuP2fH+HOA3IZ6xZ+I7BLvPC+zs\nQpcZ2qX9KTXDYI+odO39J+1E5ft1JMMFWl1FnZ/FApYYVtNJ1H8eqGmSm9Vv\n2VSTVgwWA/JwxCXzdJqHLG2zMN4DBZgISGdgU9g1OT/HCjMr1gG9efCmpzTQ\njgyqkrp8O4tMpchFXJy37V7nNWPOU83MvQa1hgrIUHUlFfhEGengo7FpBZQ4\n7ddV\r\n=+KbV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCLudYrvTSZlg6nZ6Glmg6je7OQiyNNwuSIPYpQEiLYdAIgDYQOzD3L/dKvpjEI1uGI6ONoQHBKN4r5hz0WdLX/u1U="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.2-canary.83.4d2d73245d2244318efa37864b47325b19935288.0_1613383348322_0.15441699668867281"},"_hasShrinkwrap":false},"2.9.2":{"name":"@sberdevices/assistant-client","version":"2.9.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a83a44ca60ee3ffb8d42e06fae3262b337e9ea67","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.2","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-jOx+YKAj0PCrXg1h6/3WpRdb56ZMrzigAfgjIHtrSdF+sZmBJQU03u6Zsyp3CTjj0Z805iNu1pei/PY4MEWaGQ==","shasum":"149a14e0ac81345696a0f034fe69a0bbaa5f66b7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.2.tgz","fileCount":48,"unpackedSize":883909,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgKlNSCRA9TVsSAnZWagAAnXAP/jsvBbFALdNj5k28gg4b\n/hO9JWNezpWDxCwPVgETTXoqVqbqSWv/iQALyHSSmm2I0hmXVnBsJFIjDJFM\n+LXoMSCsNHzpQGlNC7UvsrVy2zd9ycP2CmplRv35YoAzsuy3mHLJZRzxlMPn\n3I63RYxt2K6Wwp5W3xBr5nELx9qTrwCLUefHxk2tdIwfk4sEEKvpqdnpfxi7\nxotvEfvxIrZdwblGp7NQ1m8Cb5BRc59eqE2fqGlX4N7lwF4wE4BaFB8rDwaU\nWQmKkDJz/z+QRpDfJeL8Kr/rflynYli+VsTvps57E8uMA7ELPgl5WigZgwgW\nWVqn6oljaqmryNOP5UuIdY+ACH7XXbaIhWAVYILvGABTL0+DiMvEp0IvDG+R\nN7c3Gv/X4V/Kh2EmPwBhdyJpSAsfxfVCM9g+oqA3+WUF3tOYSx13JhmD/wTK\n2sJuLhJrHDH/gTkBhHf5dmO3zACYjpfRKhanTaP7Qx79N5CgIeDulQDjfYAx\nN4P9wj/qH4TGgeD2mRs5lpSS5Rz3ZC6PmqWEU2+CPZQMAOtrrw8z6QSBK5Dh\nY332zLegRUjyf0Qi5d3plNkTZjBDhDHdrbnHBqr8QUeJ+GbWKB8ZyUUrDEKo\nUGTLNer42tekJAA+9ZOlyH1uokkYEjr0xNQ8sykAbVFQpXXlbgbIuIh2ZUXL\nwa8I\r\n=cmhS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBQA9ap2LL7EkKftEYS/kKwfMg5MV0fu3nuyJFsVmdWSAiEA7sE1TZ/Pe1Yefm+Xme1WY22l9z0lJMaPNPURUmwigAw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.2_1613386577537_0.493807025198987"},"_hasShrinkwrap":false},"2.9.3-canary.85.5b834c33be8cb79be2c54d122c40b78e601732a5.0":{"name":"@sberdevices/assistant-client","version":"2.9.3-canary.85.5b834c33be8cb79be2c54d122c40b78e601732a5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5b834c33be8cb79be2c54d122c40b78e601732a5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.3-canary.85.5b834c33be8cb79be2c54d122c40b78e601732a5.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-FAzjAMTf8ruiQ3qNzn/RJb+Fmx/CDFz3mpuwFKv0PANppro71SAFeYbWST9DMstfoBb2XOmRCAvB712+RxJ8gg==","shasum":"c81c4ae3e206312b0896553dc8d21accf51d74a7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.3-canary.85.5b834c33be8cb79be2c54d122c40b78e601732a5.0.tgz","fileCount":48,"unpackedSize":892119,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLij8CRA9TVsSAnZWagAAckEP/iC3Gwo8a3Jpguk+XAdc\ncBkQ3YmlSVSLnPBJlEa3NoRjKp26L/K8rLvvopx6Mb1NvCKsLZIM7v5N230m\noGlUVasNzvH82sWydd0/VcJ7fO5H+r54WZeyllrutBP8n6YVQuxbjoc6d1hx\njn724MxRQCZU+8v641TH8VuiPVJlKc9h4E4G/DTopz4eNtsupY4zLDmtEnuX\nFaSD+5cpF7IQYdUmcX00PNvbtJhzSMSh2OaP8OLToQVlQK64uPkMhtozH33R\nfQkGjlgZ151Hp/0XrtBqxn6jqtNA5zxWFZ8491Erq3623J5XZrwWKRG1HPat\nOo+MvNzwTcWyk1eGZMwFkAeqDvZ9nOS+0KZp4hsfoOgm9cjbevR4Cwjgulqn\n7tOExYZeFSqtN72dIJM45kWwWEQCkv13Np9fj8Qq0WY63Tvj7sqcV++NZDM3\nc3xyYoYoOPdCs+KgXxgadTFNWBajot2kTWU84fCqTGw+GQnpgM1QtZdrX3Lw\nWnRLRSMrfG9hXTdNZGWLrEfjcBPQFIvdThpXiWiAufwzgeJJl/V7Dc+z0ced\n9Lp5vOhB/laYOY59CycLcEJvIeJZqJOZ2RBnPofWs7B4Fl2SsT5Kq9Me/CJU\nco3XzONJ2OEilc1PPQPMo37wjA99hy5BAHe1NeaN2mTSQKf8w3WihrIQ7YIc\n7aUb\r\n=Xl16\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHsWb/0wN1ZSND+2kojEZ0HTgp7eQyNA3St9fFLf/z7AAiAyfeGpc4QbGRNgurcz2XqklFvqsja7+KLNxPfC8q8XDg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.3-canary.85.5b834c33be8cb79be2c54d122c40b78e601732a5.0_1613637883977_0.38153533564194775"},"_hasShrinkwrap":false},"2.9.3-canary.85.e73dd38866298f779aeb4f3fa6179f075cefc6d5.0":{"name":"@sberdevices/assistant-client","version":"2.9.3-canary.85.e73dd38866298f779aeb4f3fa6179f075cefc6d5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e73dd38866298f779aeb4f3fa6179f075cefc6d5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.3-canary.85.e73dd38866298f779aeb4f3fa6179f075cefc6d5.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-XQIMiZZD6sB4/j9cZDKl/4aAhviGkKWMhcL+hVKuDkmZxWpaEH+V6g4V+r2XAQAXOTzPQrcFr6uInoMtjYTTWw==","shasum":"3d5d698e3da6267f88d2f2ad8a5865d5de30c44a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.3-canary.85.e73dd38866298f779aeb4f3fa6179f075cefc6d5.0.tgz","fileCount":48,"unpackedSize":892345,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLi+jCRA9TVsSAnZWagAAuOoQAKCfm4GU/EzxoUhpQaGa\n/6LTKxZPla6zlVSaZzda9rXMDTr1tzLuNal4ZIkSY5nlpSONM/Fse2QLGSBf\njtEvcfbeQbZuUoo4MqNHNylEGIsmskJGxsxjcU4HiMAi6P5Dw7tAw6pQs9jg\n2UPppFPwuAdsMVhBdAb0NbHIHPS9fnK+wyNsKQsVksnZJrQ+7e6LYW8C9/OQ\nQRJ8kHOWNFXNylrqsJMF4873bcpEOrPXHaB5elq9aMXtE1FL2H2ERbHsXvjX\nxNal5g//Td8YE6ApUpanHK8N1O54u8i71ishOpDzt8lt8x2B8mtVpCumEHCw\nFQC/6+m1x8VPP7WoaR6jIkMgynzjxp3W/Tdv8qYgk3QbCriqHiy9G7k7/Eru\n5ApUxANYoVDy6FP3hhIJjANlC+r6nae6mFo7vuF9AzQaPbiycYtqsu3w/qBc\nw+46wdJddb2bCuxWhEPAQcb8BdBcdxFpzOVWHzd8mM0UIWmO+5IaSk19b1fa\nVOliNq0jnNZIgxLcQyvpyqblexx2GTQU12QB8qha0PfSwsNUBW1zAHbQ9m2l\nk2RjrG/MjJsnrliJwwvveOrvsXzAmzllwHJ6P2HW/BULNu/3UU1MPGxATo2o\nd35REEtWuPYSW9TSFsWsK+MK/Xj38IWVkGgQjLxE8K7UaVaKCX1i5RvDqljW\nFVly\r\n=EdQm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC+7ZlCB5rOKeof349lK+c4TuFXwtwF+OMLt9oI9zFBvAiEA3LRSpR/UqeNMygVIwlkbbg5NrKnMy6pL+wH6y6qsj2U="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.3-canary.85.e73dd38866298f779aeb4f3fa6179f075cefc6d5.0_1613639586719_0.17229672374724103"},"_hasShrinkwrap":false},"2.9.3-canary.85.fd8141f9a6fdca257e4de6189d1259f2b5e65c66.0":{"name":"@sberdevices/assistant-client","version":"2.9.3-canary.85.fd8141f9a6fdca257e4de6189d1259f2b5e65c66.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"fd8141f9a6fdca257e4de6189d1259f2b5e65c66","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.3-canary.85.fd8141f9a6fdca257e4de6189d1259f2b5e65c66.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-muIpBUIQMbP4ye8nrl6uXKZqydC7hiJW2SnFYTvvEbnIRmitR0YpocPm2gEo9xFrISYHnwcfmDCEqhRj/lOeOA==","shasum":"e6640202c9894202fabadde51218a00537571781","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.3-canary.85.fd8141f9a6fdca257e4de6189d1259f2b5e65c66.0.tgz","fileCount":48,"unpackedSize":892739,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLjS4CRA9TVsSAnZWagAAmGUP/jKRjKtvsFKsfs8KiAlM\n4esPCUyDQyvli5X9//wwEtx0hDVnOu4Ez936I5M2jNN8df+Izma0x5X97vKt\nGRWGEiLhjgI4JyV+UyTRKii37GJWVF7K41m4EyTMSpjZxpYmuTE0eAdof8BX\nyIOcTZILR4xQRjVzEcdiCxRyA2cszi8vKTeSCqghlXWweKmK6s6M/SnXw25+\n+rwPOQresZRseeWyma31uv2LZkjnCAjE+YW2TvkfhUlPxzmzD2ggn/CqgkUS\nxJggbJFfNIAkFxa7YujTJ+dPIQed1vyXAQ6vlljTnxrjxZPvZtWjnmHv0XRQ\nhe2/deFhX5W3OQRqFdlak6XBwngweUzSCEXzOSEA8Vwo5+jntciVpHTKDs4U\n9nzmJUAiPhsZ80UJ+Uv8Qz3cikGntAgSHsuBmshwwFJ/o095y6GjTqiMHnt/\nJn0v+YWY6vVb06tq7PTnaUCFpgfjIaVEp9lmFXOAZR7crMhoIeAR3I98FxJ9\nlfbE7W67B184BBRE3ahtjCmuHpzLF4NiR9zZR9b5nxasQV9i8svtnEL8AH9f\nR2THmfW9UyNM4kPrS1ve9iVtc7fCTVFYwGccOwhLnpe1iXAr8X0faId3ZQ2o\nGUMhDeTRHym10tYQ5ex0Le1eNvcVy+hgXi60oOWr+ynA12bcmFv24lDHkTQe\nZE0K\r\n=bzbt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH9fK1p/kUtd7EG2r9alDl7ci+fZ0qTEhkSovalWgOTOAiB/1JCkYMjjO5+2Jwwb4ZgFx0fsOAv5tKmi/BJMD1jTbw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.3-canary.85.fd8141f9a6fdca257e4de6189d1259f2b5e65c66.0_1613640888123_0.028539577805413385"},"_hasShrinkwrap":false},"2.9.3-canary.85.ec032406c7888a6384b571ef01e9e5f8ceb93fa0.0":{"name":"@sberdevices/assistant-client","version":"2.9.3-canary.85.ec032406c7888a6384b571ef01e9e5f8ceb93fa0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ec032406c7888a6384b571ef01e9e5f8ceb93fa0","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.3-canary.85.ec032406c7888a6384b571ef01e9e5f8ceb93fa0.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-QVY4RdjbKkjYy/hbsz9dAslcySBb4EW9X0pX7AZAGj+8mZ6KRndZep2WmAXHfYYHHSfXk6O9wqV5ksSfDydZUg==","shasum":"9da43714d715fa077f736b8fe2d432f87b065132","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.3-canary.85.ec032406c7888a6384b571ef01e9e5f8ceb93fa0.0.tgz","fileCount":48,"unpackedSize":892865,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLjebCRA9TVsSAnZWagAAYGIP/1eage74CwwaqpSirJOQ\nPk8YoVEjgyozSLd7GNyHKS2xHueWOJwhMw2TkqaFQF6rsXQRyRM4JImNa2gR\n0f/38tMijne4oMdKURGgn2QLTgLebUqvfga7omi1j6lng6G2J/ObWlbdwE5A\nlamCG9CFosXif5koQn0UETpkEdCrdwxUWmo3poSSJcoekWQQxCv7dXfRC81x\nnBcl+pfzeIa8/tDa81wndECKla66H/ZLXtnyBXVEQpUUUbTKFEzOCCSuY/Hx\n7r77GQ5EkibaUBkPF6routgkzg9sTLAwSGP7D7FWHiRH4aKoSCk+Q/mb8iVQ\n8yP2nb4qvfvfmYU65zr6tsjiHlxz5UeHFKBaJA4t6ZzG1S4z2C2P2T/C1Wfl\nneW5xOjqVw/3/jBKau3hHRoMsNceHIxOtcGcOmNasOVY+lYh/iNUt3KjaADX\nlgaJrWIANo9fw7TGy58lN6sxlfd4AI/1hlWWFujkkINMB4xNkgr0GpPlE6n5\njk1otl/JiHG1CX2OU3tPd3LCvwWP948NUKNJJLGl9PAKkDurjScAGv+CLoTO\n3SElHBlI3jRawJkPxHrcA8y8W4V2DOjNzLEXKZtEvt9elKreFKrG11ejyaIy\nf8keFzG193JlSAJ6WDZqVzUJyfCgpzoTIpRmyhywM7uxL6ne0y0AbTKfOsBj\nws63\r\n=NM3F\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCja+o5XZuyXak7SWVmwS3YC+nb/0OT4oEM5dLiSmovUQIhAPyvhyOYmovbw0XFuUXeKn+E3e2YftmV8eFWq4RkwhsE"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.3-canary.85.ec032406c7888a6384b571ef01e9e5f8ceb93fa0.0_1613641627000_0.06814380238816686"},"_hasShrinkwrap":false},"2.9.3":{"name":"@sberdevices/assistant-client","version":"2.9.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6c2e7ddbca2475fd0cac0d142a9bc4b4ada4ecd1","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.3","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-HXfLQ7uPd1IKsKc2W8Pz5anRfXtyWoNaDhU2F+t465HBMysHx/2nKYHpS1aF30lhwV/H6cXP5sbcPZpuFh99Jg==","shasum":"d4f2a0caa40eeb4464d31dbded1f917488314cea","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.3.tgz","fileCount":48,"unpackedSize":892996,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLkKgCRA9TVsSAnZWagAADqYP/RyVO16DbbVohjvpnUmh\nKXRD998XwV3Zl4F6ySP21OR8YMrRHsdODwOUybHii2Cpv8p7j0pe9rptJYLq\nnd3HUWcaphOHBxWlD9O6idvG9MeXqgN/Xz1dRC08yZQfPl998RHTYfDhfOdS\nVC8c9Kin/8EEeDScwjBYm4G+UZiQVwIZ2NwALsZpJPUIawjqZluWSkRcwbdM\nLGgtbVKMobPrTZr2nO8N/SNZZdmQTTl9Gbzw3PBaLflzmHL5YLR+XYi9vxtB\nTDWgogKjygoCFe2+PC530B2Gbw7NPZfe2N2LTUdBgTPZGS5hP2Qmlg1IWVLv\nZTIqhTe37z9BHkEHXqoMDPgp9iSpYDd0Os+xHbpK4YkqoHg8BqBbrk1cxnYE\nMYJRHhQhiQIUW04lW1KykBVACcL/othbRCA0YOl1EB5GfuYFCbwEfCTCpjnT\ng1yAj2lF9bpZ7ZksQc3/OH4rUqj93yWwahHb1o1k3B49nDGECvqPVf/g59D4\nvkdUFvDyxptnkNKmNt+RVq1FLp5JXr93yK1SlpH7i/XV4JTEwad07wBTdrSK\nqKCAIGWXE4+eO0dyYC/rPWe99L2Ahb3mvwwMjCpROV6Xm/OADzApNnE2kulE\n7MGj99VJPSWqXl9BXN9FlIkcmDHGRyf8PyukZp2Xauy/sGm6zOvA888VNIhW\nSeoV\r\n=gvXP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIwRv5oIDQn7e7TIe/da178hTqgrp/wR1pj9Z6yULsrAIhAN9uqBUp25UzuJ0IA1p9+G/5b75u4CvfMOclG9bnlKPV"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.3_1613644447773_0.6500230373553166"},"_hasShrinkwrap":false},"2.10.0-canary.79.ebc13d1f0246a4ce4254f1517b50f8550c51f43f.0":{"name":"@sberdevices/assistant-client","version":"2.10.0-canary.79.ebc13d1f0246a4ce4254f1517b50f8550c51f43f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ebc13d1f0246a4ce4254f1517b50f8550c51f43f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.0-canary.79.ebc13d1f0246a4ce4254f1517b50f8550c51f43f.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-uNXO7zJ/y9J6SE1Yk4knZ44D6PJF/6Z/Eunn1zDKM7ZAnLLxNp20Ym6/0eItuXiDKOWjKW8Z5qVOalQCEAVoJA==","shasum":"8fa467769786aaf27bf7ecb65115fb005260dbf3","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.0-canary.79.ebc13d1f0246a4ce4254f1517b50f8550c51f43f.0.tgz","fileCount":50,"unpackedSize":898568,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLlK4CRA9TVsSAnZWagAAe80P/RtVZNbDnyPB4Uz1v9CE\nn6S7/De1id6CmxFWIGmcWtpI+I8/Bhr1LMk7lXSUckUMYZQMH6cvTKm6D5a6\nbfC2bpIX0y/3SL3MzZtGtx3t5bbL0NQrk92+/4VffBYVan2HNRXE3KJB8uiE\nmc77VFYW4hsd0YwJh8UPrghgSjyytNS+6U8YcYR2Sdxiew95trD7PcRp2kaY\nkElTr1Ny3PfALVJGjFkF2JiuK3RQST0TOjXQYEfqKqGiTHEO+px7MZ2H1OtY\nhe4RsrvtLJLmr84lm9NYKpOVQS3NGgBym5XAW5iThBTXtffkifYtZeMiRsDE\nL94/j1cTgVLW3EA6Qt4C4LIAGoIjypIOtwg0TJmWHyBzavVQMqejxDBUuQbs\ncRx5gqKslOFWC0FFmaNDgTGwb8yl0QF2Ujwz76EZ0wJcLtKPXk4KfnDIZ8Bp\nw0q4wpkTLn5zWylEwiC2Hcpo5upZDCF47R+lFv7WMcEJPAtnGjCDXpvqMkwS\n0dvITl9fImMvvnIWjcIWquwKke1rWjf9dRWKmSTYhdGprvcfKn7jWWhJWLxI\nLLOYTjc8yh3VAYU/i1jduqXd8Qx/CBmByDySlifbpPuFAOo0CMGJ/xo92T2+\nHAxYIGpyolXqXb4XWHKmQo+18Je+ee86jXaGS3eXByOvUFea0Uk2CYkD4uHZ\nQU+6\r\n=qhf9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGda5AcIfsokK2yZHsLfv0Fg37hf1iRy+xVBXRsIc2WSAiBEBOswjb+utgda8PHc5dEMeOc8Xq8bBCnPSbQHlrDNCA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.0-canary.79.ebc13d1f0246a4ce4254f1517b50f8550c51f43f.0_1613648567876_0.7228973238276706"},"_hasShrinkwrap":false},"2.10.0-canary.79.b2008989f468d625a8d79d67822aff95889867d5.0":{"name":"@sberdevices/assistant-client","version":"2.10.0-canary.79.b2008989f468d625a8d79d67822aff95889867d5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b2008989f468d625a8d79d67822aff95889867d5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.0-canary.79.b2008989f468d625a8d79d67822aff95889867d5.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-kz4eZuW9cx5qcU2tvBpT094FLUfnO4yqroZ99TLVneFHSTx2p1Hb0LoXzcuvIbUdpmcdIVMnWcX2aysQXaZuxw==","shasum":"16843ec05495a0608bf36f3a396b485c2dd1523c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.0-canary.79.b2008989f468d625a8d79d67822aff95889867d5.0.tgz","fileCount":50,"unpackedSize":898568,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLlTMCRA9TVsSAnZWagAAl5QQAIs+TaK5TXeFo2TPFpSS\n/0kaf2l2gYWZ0RB6z3voLMoYcjVyeC8j+8V6iM9iH0vdn4CH3E21WR+kIcmm\nQrCLYmPQhx0vK53MW3i6QQnqIT6ed/6X6X4RJFfW24v9J0t9TZUYud2PmLDS\nDrGcT6r0+l0rF2Z1B//bQu/+YSc5AbCpxpXXhq7kifTYz9pWzQezJNsJFW9g\n1yz0N1UsW1rD4cUQwM2uDyxxIO+8OVlPIKZfU72ooeOHKgTL4SxqSuj5ywAG\nZ7LAphsa1TzkUZQJBnMHcYMkWizdthD3j4+BBjEsOlzZvWeeHcnppmYfyVre\nFc7cwu22iNWejBKLpJ4jxcV1e6vOrMLj2+FO8XjE7yBXhmN9+PBuXxKaq3lg\nrKjntJ0ezpC/ja8kNDvLlEfHnElhBV1pHtkaLnTcVe+szz0gpP04ZElxEUip\nEqGB2FzhLwqVNz6yapFaARvq88dHSubh5Wa2Psts8NHe990fEnYkoLp628Y5\n+pdfsE90KYFfpKrmZGB71YWxiFwJmfPAcAvb1v86newoAB//0DX46wOrODvA\nvkFUvFVbavBvU0yfcWZJoli8ZS3EqRViKtQ4ndF9De9jQJSqLGPM95gK4dhr\nyZzmzweQh6xpJ7VBHnr8VKBJ3vVIpXOrDmUYx7uyhzdl58JUcKOUj1WSL5ka\n3KOp\r\n=a7VN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFqr9eqM8LB4wq+V58Wan71fvEnM1D+bSs4M541TtuoXAiEAyGU07wPTARAfDXzTa28ycz3m4ubLSco5kbG1Oy6s1LE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.0-canary.79.b2008989f468d625a8d79d67822aff95889867d5.0_1613649099544_0.6476055521929964"},"_hasShrinkwrap":false},"2.10.0-canary.79.52bfe5a7f354c4a3c5b5953837274d003ede93db.0":{"name":"@sberdevices/assistant-client","version":"2.10.0-canary.79.52bfe5a7f354c4a3c5b5953837274d003ede93db.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"52bfe5a7f354c4a3c5b5953837274d003ede93db","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.0-canary.79.52bfe5a7f354c4a3c5b5953837274d003ede93db.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-q9MXsFKmlekZL7E//spR0V3h8N8uS82hwgBF4CI7DY0a+Njrv5dq7I4gyJ7wc85Ex1afSS9IxMUoPAUmV7htgw==","shasum":"3632c2e397a6e64e795905490ddccfea921e84a7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.0-canary.79.52bfe5a7f354c4a3c5b5953837274d003ede93db.0.tgz","fileCount":50,"unpackedSize":898541,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLlVMCRA9TVsSAnZWagAArIoP/iIwEmaPKUCtnvNveuv+\n5P9MEohtconrBUCXpTLtTPF5wsdADi9yaSzS0ic1P3G65TVrwoM99riDKO7B\nGQej6xd+ePcu0MYbbSgsq099r/dCVDfTbNN+EB4IUvS1wFYe9WWsKHTx2RZ9\nOxMXkoHSGoVkg3i6E+C0Vdnl65T6cpu3yd9OFj5v5uvK2bWRfPWjk4yFLBaU\nU5sOLioy3XJmMsQg07J1grPuymV9tZOXKBgtHzaxQxcR+EL7uao2A3ce9O4s\nqZZLosPMlKTz7BZgIi3ZchAnN/3KV+sYM7qkRpKb7Eghwc6pBK7fdp0ITswX\nj+07vdFQulpXrq6EX+bFiBMaryPa8sDLkhFn8NADtTpZlLYyuOECuYEJvLIB\nQDnFMgxTqM+nwWR3YLnM5ElRspWQyLJP7JntthZW9ZkJNAk6LTF06ifhDMVK\n5Gw+7V37L1/AoLXof9x2QG4LN9NDMPv989dx3KJ1khH45QbN6OJJ4w2ZgHRB\nhyeU6i07mJz0r9pJFCQAspLMqO1uMWCXk5BzIjeYMG90rfdMj7uR39KrfojD\nBV+6V8gaF6Gt1G6QXdvd5M9QqvElIxUhrKIra02deXF5JCbjyDrFTzucGfaB\nHEQmPJWYy4rOk0W0PXENULMy/Ak68i+JSagcRwITqQvFxeHTF0EhwDgFakpz\n4gYD\r\n=vqCD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHRaSuaVIg557Jhk6dn6dEvcy2x/9TNUiJgko7mHuz+AAiAK2pwU3pQAhaRjImvo3aOGVl8Luu+5Vv+hJ8zwDbLS4w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.0-canary.79.52bfe5a7f354c4a3c5b5953837274d003ede93db.0_1613649227799_0.07980409426523516"},"_hasShrinkwrap":false},"2.9.4-canary.86.f67a998f06d936633336655947b52ad2afc6c772.0":{"name":"@sberdevices/assistant-client","version":"2.9.4-canary.86.f67a998f06d936633336655947b52ad2afc6c772.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f67a998f06d936633336655947b52ad2afc6c772","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction) }): void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.4-canary.86.f67a998f06d936633336655947b52ad2afc6c772.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-9j4d9/qLj9vj5Pn7o5Lw9spGdImF9Gx+V//ocLJGy8n5yMdnM8wg9aew98BQagYag17jmvZOll8mJLIrJ/KrUQ==","shasum":"8b9bf78abb1ff159a508237edcefd524be9277f7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.4-canary.86.f67a998f06d936633336655947b52ad2afc6c772.0.tgz","fileCount":48,"unpackedSize":893535,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgL2ePCRA9TVsSAnZWagAAxNAQAKJdzqnW6vwOPplFnovI\ntfOhZerdTyvmFDgbi02IBFB4dQjKY8rUejdcicvBZ0StyzDWf3KD1MUevEca\nkFZzjThc9NaNyQCLivBC854B58xbD9bUCfFJ5yig2hZ6pkMAYrZN7EWAIlcU\ngD3NIt2yYe96QHJxJaqHlSJaW2kWHtlRI9S5rjrGfQWUzEfyv0a3xt8mcUP0\nhtUr/XtN2jvkHvf74MYNgsuNB2AxZI7HvA9oDkUAe/0VRBBZhOhIfuYoc6n/\nlSwkB0IdPmxZw8N0nGOPzQISNam+RylTmLZCnWAv9owe6Roj6SBGvygj+xPq\nOXcSvDHy5HrNYmurkRg1OLUt63Q0g9r3OQ54IxWqHFf0ZstJECzUMKI1V+Ni\n991Sj+dTESvefReGdYkcm5QWuH8UZETnxrv+/q96TZ1Zw2gwK3jxhDgZZNFe\nCpzCfwRADFmvx41O7OPuEbI7wc6Bjrauzcs5aGt7/mTcWvCyH07wINaE1hQ7\nkCbYeH9pMczCnjRisDo/CDKH4PE4mGNpVJ6hVtOOfDLrvJ95hN2DT8fy3xpZ\nJ2mtYY1ytu3jVuxSlQ84nQwhJnE8o+IsI+eN0XacSgjxxxWtAer4xr9trI3D\ntVgW774rXJ0FAT7n7JG0dw0cIKOCev060ViBbh1dKA+qDU/dMd/CMmEmgwFo\nDgM3\r\n=xJow\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICZcyX6swMOqA8qf6ZLQDjiU8i9CxFCn4mcggHeC6GA+AiEAy5u3y8QcDz3Yq2Vhk2I6IbgXpWOlI/7cItgUuh16mWw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.4-canary.86.f67a998f06d936633336655947b52ad2afc6c772.0_1613719439257_0.15026670571596767"},"_hasShrinkwrap":false},"2.9.4":{"name":"@sberdevices/assistant-client","version":"2.9.4","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","nanoevents":"5.1.7","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0a5c44c3692edf3a9699414ed914ed48219ff4af","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.9.4","_nodeVersion":"12.20.1","_npmVersion":"6.14.10","dist":{"integrity":"sha512-U+4549zhWt4SHzGTRMlXESpYtIZHP9/y0m1tqnm0vieo3yyiL6g1Bp7vulytJ/MqblqD4Dnnbu2a/vT+Hft3Qw==","shasum":"edc5e47b0691a1c4ec757cdb1c73b979b3299d2a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.9.4.tgz","fileCount":48,"unpackedSize":893653,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgL2qJCRA9TVsSAnZWagAAIRwP/0yd7ELonjRQ/DDOIq0e\nzCIfWcfaympbaNBI+RkHTXJyV/GB50oSS82nMptcv8XIxY7mThGIQ4Ye5qVQ\nyRXsD5XfFQBixNza6wz0haPxWmKFTO0lOWALpZJ9EYd0RgVKmxDaiBgJHCbm\nKn9fDuE6TqFoy/dtKTPE/xCQszynwkyJH4IPkLxOvW+oQgcFSu4zzwhhd/N+\ntzY98AvcsCZk8L+IfgAyrrOyNF+mjrSPAiycFK6fHRGYn6LRFa+v2qQQJ2xb\n1DukCMnno3vPdXe1aTVhQ/yQjuT0V3OcC9AwG8LwoDOA8zT8X/TIcANGAdq3\nKYVfEi7hBDcKrgi3Nm6xvKOGAVcKVfmkd6j+dPjWMJcPaD52vGuIPGDS2zcz\nFfR4NvER+tFQKSBg5ksTkMW+Z7RQE6WEMnx9C+MPuBr9BSwwqZhXownz5YW8\nHVDkwfWgSAr9q3kkBnrPcA4tWwnG2IeQhqzmmVA4TdEsmBIfM6dbPKODhhuS\nBaWDyutynowEmVna+nBwvPNgGaA8A+6BvRv0Va6rDFSrM0Kl8wd9gsogBPWL\nqzrQutVJVpboxi7/cbN0iYNJGPgcdkGHDrbwT0NN/1lUbaduI+qGqTx6vAkQ\nEDUBXhSFJ5bdhQSV1xpFB74Lf9YafJPTndpwKo3I4nPBzbxT1y3GX7qZBS5h\nUM6B\r\n=JbI0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAjbK1Q/Kpyo/UoU0FdebVihY7IJS+t9Om5o0lDrymNOAiAgr5kPhu35z6sSmmRLsMneSOL+lqd8PMUFYBtnSkdAfg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.9.4_1613720200368_0.37037505468733145"},"_hasShrinkwrap":false},"2.10.0":{"name":"@sberdevices/assistant-client","version":"2.10.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c1283a928f22975472f59f81e7dc50aec48b9e4e","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.0","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-4ptMIrAW/pHAUTphpju88rOKs+cbsXTfELnGW4GgkndhYJsDJFZIx9+WEy/LFZD3TJfYmINmqud0/R/1lbxQfA==","shasum":"9ee7e8b2aadf740fcae18cd8fa83a2ecc93dff4a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.0.tgz","fileCount":50,"unpackedSize":901830,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNgS6CRA9TVsSAnZWagAA2uAP/3gDpbH2ufAon2BjvJUf\nj04t3A3Wv6bHnSbMX2fzI9INuRr9DLVDhSY1ehPnnMRCGsSKNj8s8L495d/6\n/spUo9G9xGBP7vESqpOyUML8yIADPjgendff/se42Vt6NBawwrjJfvjSwod+\nKFWjjYtuUX2B0LbVWBNCLTwGAW9G2yyq1QkpApUmSJwffNSjrJ/7ysVKhYY8\nqyvRN5eEZGR55tYgCboUwG5pJlkFCKKwm0bL7ia2/dE2GhRVY9WfhJyGNCRz\nQBtsf/yGwjF+EgJiIrqyT2243qi3WdwVKw58gluGT4zk5j1NSOe6EM34yzr+\nS8px/3HWxoOk2ctQb5hHFkQEHVV8GNZiUvJEaRkgCgnlSMWz0KYanjPcNfk0\nJfjldIrGdR3DBm3vWDx1DYaqzYlqBvR+N07FKztm2cIWgprtaRDZ6CKnPovi\nnNTZD7TU5GlwqPUVRkiXIBhXFDn2JzRroPK0u6EPTGY+fvHKjQm8yDhOzQat\n2vaWNp2jNFZBDYfqc/jZAJqZFS4g3YWvrV0aAwaXsQd3HbQK5FV2l6UeE1Un\noFXzg02jA3LbhR4+uNxhi9e7ZWGpMjs/FaYQIQsa0lWy3w4lLRd7SuHy3QhE\nzUF1ejIfwxCfLqhm2KKIh6HaSR4vTZlfHDbLKska3TDjSitdkGRgzNqrSmfU\np0H+\r\n=KyY0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIQDnR0h5FxAvnwUo2zF3EQhaKCSI2S75HhWEFTa28FCKiwIfWLL6IMIRJL1BFYRuTFzEKYDQcYGWvCQ5JKpNJgUfhg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.0_1614152888480_0.19966843656264954"},"_hasShrinkwrap":false},"2.11.0-canary.89.95e554f3fdb1110d7a9fc35e732dbf8e43983817.0":{"name":"@sberdevices/assistant-client","version":"2.11.0-canary.89.95e554f3fdb1110d7a9fc35e732dbf8e43983817.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"95e554f3fdb1110d7a9fc35e732dbf8e43983817","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.0-canary.89.95e554f3fdb1110d7a9fc35e732dbf8e43983817.0","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-ZUlMZyE6Y0FBI8fneiuuPPXljgw/22FoRrKMOD8aoFAY594/lHeP2GbhosUysaWsulnKKRPeoc084gbZyeEl8Q==","shasum":"4916c44723be319077626eb56ebc7133ae937d0c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.0-canary.89.95e554f3fdb1110d7a9fc35e732dbf8e43983817.0.tgz","fileCount":50,"unpackedSize":905068,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgN3dSCRA9TVsSAnZWagAA/RQQAJviuRWdsfbNKD6YYbtD\nCCz8q9lonh9Csr8H6Aeub5Iv5PNlxbQq+oYUEJYbmAq8m80uWvDLA3TmDPIT\n4EHpE7A+5U7jci5q3OoIMs766cFHcBtkW8ox7aL6gshOdR5t4gj0GsW1eVzV\nyAPkC/bgRXr7AsGSsY1F//dubYx3f/uwtfVrRQhnniIiujgM3VgivpE2bF79\nLihATM6alsmkVZtWmI39pPJnWFJu2whAAI/0R/iPaT4f/N4UpwAxDpQFAooX\nirN+e0FJi/Gv+pNw1F0lyUelAajYKcYcHwbEEyBXm/H8iwKChKOcIKIIC7Bb\nGBQCy19K/BAFfduHsZHSY012l4ADd1LMGJoN8fjb5fswUeaXBLkMEXRmmF6l\naTUjc8Y9SNvvJd2NAaJYNwk0+UIcZ/Yxw9fty8cY0feg5gyNgmS8HwqZZbNW\nubuFeBceDEF/+v2BZbE1lbSK2hpLaMpg0qaqpKqoGOHz4wKPVhGrUJ7A7Zd6\nk5mKUAXxk4VqG1J39snlJuHDVnSJL9uIU1Jek5W+WhnrhKj7jNuNTgEapT0b\njK/CDoHvgG1lv1kCKh1FxuA12jnMz0flg0Yd9nu/iGbMLh/LZRv1WPctAmXC\nEyQfHesgZGXAp0AQxgQOWMMA4JbHwAljsRJwPvA8XYRy1btANl7HhPULJ6ZM\nOyKG\r\n=ENn8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAJiVrzwWJC4FqJKKToGa91eMVW4EA5ZLJHM7d4ZtJ42AiBhVJuCkROoRpVptdts67Ed8P4mxcQi+qJAyA9xdVyWKg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.0-canary.89.95e554f3fdb1110d7a9fc35e732dbf8e43983817.0_1614247761871_0.6201998554441019"},"_hasShrinkwrap":false},"2.11.0-canary.89.0a9a6d3a666379dee88f3368a0760a43519468fa.0":{"name":"@sberdevices/assistant-client","version":"2.11.0-canary.89.0a9a6d3a666379dee88f3368a0760a43519468fa.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0a9a6d3a666379dee88f3368a0760a43519468fa","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.0-canary.89.0a9a6d3a666379dee88f3368a0760a43519468fa.0","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-j3tvOzYwn/vwxuYAftHW+L9P+qQD6+YCU7wBTSvuCyYL4ePAbjFqnqR1ioA1wq/9/pCBaFM7SrICJrm/oMcp3w==","shasum":"934c1ed32e57444853f8d089ab7d2ece1f25f269","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.0-canary.89.0a9a6d3a666379dee88f3368a0760a43519468fa.0.tgz","fileCount":50,"unpackedSize":906402,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgPKxFCRA9TVsSAnZWagAAJqMP/3QvxsfYd9z2yvjnIJFE\njRbaVhDayeQ4Q7bOKONphlKB9mn8xIfs0QgnGcCWanb8++QoPFVHa4dMMh7U\nQhlRzNFdaXvQc9ZnB5WKolJRhLS5PbMq8pqKUuFSCBFo+PoopzO73abva4rn\ngTA0gGLAaQS7S7SUCA5V38O5X4lkTMIhOQwleERiYkCDFQtlNm9nyBKp7JfA\nql2qj01Xha9fW/0TOn7Yo/x7vWxtL1x2UdrqwauwtqPrBoSmqQJx1BBzoQz6\nX3FsDD3eL6PPfdT0Fpy2de2PXNBVwzcj5PneEr8wFbzXPS5J1xDsHCHydfWl\n4rLXTZDE75XyIPo/52yh2vzEr8gJRd1sX+5WpracjyqiFtBjm+O+J+rQ/0L3\nRAoeAcKYmR7vRsQaKxDXwHuE+GlltJXHIpsIv49s4MDUFDdn0JY5r0LujNEf\nRoUt9oEwhJqv2qAoNG9woVg0SI588MDd/72C0v/rQiJ5Jwwn9RDijeSssUtr\nS5oNmqiLQFo72sttIbjmOjIPReN+zz8hKRlEhpqpugLg5vC8LIhZHip11l96\nDUSauY5J3BG95llRUgm4zlWiF5oJpSz8vdSHWB2c4mhkdHiN8siBZM9sckJn\nMjVOCQp+qfOC6/1raEFR7/w9X18iA5KGRFNsvwbJ6Y2oiFomVmO+H8bQ+3X3\nG8kg\r\n=Zqoq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEODPe84if9JZtBMxUGFBVX0unXigW47CW1jbrK7tzOdAiEA9GWMx+OqEe9668m/0pJs96A0KwtRVRqsae3XsF+x1tE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.0-canary.89.0a9a6d3a666379dee88f3368a0760a43519468fa.0_1614588996868_0.06660031050064474"},"_hasShrinkwrap":false},"2.11.0-canary.89.aaa6b1e2523efeca680efde8385eb75f2be13039.0":{"name":"@sberdevices/assistant-client","version":"2.11.0-canary.89.aaa6b1e2523efeca680efde8385eb75f2be13039.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"aaa6b1e2523efeca680efde8385eb75f2be13039","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.0-canary.89.aaa6b1e2523efeca680efde8385eb75f2be13039.0","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-kqrUIy55iNYbibKlabwwX33EuQNafpNFcilB6GcC2qLXmlzwdfuxs9vD3G7wVzUdgHmLR2hile/LLlvABTELZQ==","shasum":"05436d138890a43804959769319b2bf80652fffa","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.0-canary.89.aaa6b1e2523efeca680efde8385eb75f2be13039.0.tgz","fileCount":50,"unpackedSize":907386,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgPOqvCRA9TVsSAnZWagAAm/cP/231qsrcQP6EwvxRK3xO\nIk0zA3SDLSlwwYOi7vGWUOxNvR8HtkWgJdi8rSCITktadr+GKWi2GY70cXgG\nqiPBqThoIWpQhl7XTp9nj1QzauBH5ToQp3u+h7iUtoQK/9ZatssmeJwsmfPU\ngwXEbzSWZsYGYyPaolXt9TIW+SlkekEpV+d1lOHoydJPmdUrlIJnq6YDrdCF\nRfQlzwuwFsXmaQ9dHL+IBGdkIf+1w5tIIk8yEHonwI8xtYptmDL6Aif4copX\ny56Wh22acJNWhBGHmJNSBwMIoWsN/fwT4o4Iei5YCJJLsXLTZmtZ6D7XwO8U\nFm9iC369u0mXizBxUTvWbgrtjUGmhxVNasLBZnwfNoW3ojwE3xpg5u7Bv5St\nRk+qx3+0PtMxCFRyZPJusZKlfIAjTqUB+vMitDuywMsKxN1a7QZ8NStHSeOO\nHwJr1rR/Uwn4bckHgSKICvlCJLq3U/r/pMBNEdzcxWFnj08PPbmL8yJf5RZo\ntmXGp73Rgv+IP14mCn4PbtEjt8VIkeYdqSFFIGZExfL2Ope8myqOKygc9J0p\nl6MUR9r5RACq7B4w7sF/uVgGGvNSRZEDn6Pr9w7YqaKDIEM9l28IXcBv0mzN\nEq8o3OHlSm89LAs8URfyaY/PiBwo6imuwX56ZEbzRAGW9RfSeZCvB8NGkgLn\nYUi8\r\n=XjMT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDW3i2z3MbopUoWm8vHroHNZgMHORIQW+dKVGwdfp49pAiB/0KxfdPOp/1+eRIz7TCaeFqbsGnARkflpPbzyhWfkpQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.0-canary.89.aaa6b1e2523efeca680efde8385eb75f2be13039.0_1614604974670_0.9762262327091902"},"_hasShrinkwrap":false},"2.11.0-canary.89.b36417bbf872b55bafe58bc23bd197658d71566a.0":{"name":"@sberdevices/assistant-client","version":"2.11.0-canary.89.b36417bbf872b55bafe58bc23bd197658d71566a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b36417bbf872b55bafe58bc23bd197658d71566a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    assistant.sendAction({ type: 'some_action_name', payload: { param: 'some' } })\n        .on('data', (data: { type: string; payload: Record<string, unknown> }, clear) => {\n            // здесь обработка данных, переданных от бэкенд\n            clear();\n        })\n        .on('error', (error: { code: number; description: string }, clear) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, возвращает объект с возможностью подписки на ответ бэкенда и обработки ошибки, полученной от бэкенд.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nassistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } })\n  .on('data', ({ payload }, clear) => {\n    // обработка payload.data\n    clear();\n  });\n  .on('error');\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.0-canary.89.b36417bbf872b55bafe58bc23bd197658d71566a.0","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-TZAiQgtckHyeJzfDg/rnJHmOv0whqP0f7vUCQTagxvKBHJ5FPl4fSFQG344ShMJMlKuUBtTgEFaNZt2qkcABhA==","shasum":"2f00995ced0e17f1f1c1676d9cfa278a706fcc7f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.0-canary.89.b36417bbf872b55bafe58bc23bd197658d71566a.0.tgz","fileCount":50,"unpackedSize":909324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgPPNpCRA9TVsSAnZWagAALgkP/3bzblQbo5YaNw7KrTCv\nq0+piH/DyHIlB58O+84QMglGldb5pLNAqvC0VfdaVJ4Dmz/J3aXMp/O3iXk6\nFaIY8RraSBLyX9N2HcO+gz4BINMpenVHfhypa45fXLIj933gaUkd8O/HjXpZ\nI48YXJ94M9Wi2SSnI3x5f6n2srQVKJUDOMXsnehTPjPqmts3l0aMPBLHw9V0\nBlC91rbMA04LwuUb+P6dM99AJ3DDwjc7NIoypc9hZBifjBPrM5f8e8zWZEqu\n3ULjflhdq5ro8LP6kNzKTosqjVUia/4Wcj3LMpUHFwJM+LUftiHQ3lhargjI\nzn0IuLQrAGPIKDHTxpnNpoP5iyZq4AHIPhZnY2k8WqhznUWmElwR8E07QqXb\nR0bdJHi44R7lVvdYMD+nCdgmQVPnLHlFv068Bjhtb/I6McYM+Y+yDSE25RwR\n25jzq4yKwtLsy+luLrsEgfPvPxYQtYq75phtmIM9P+oXzDR0UcRLernjx3Zg\nrKWhjcZom2bcZPaBau5aZUUX2/innSBsggQVBCHIeZUdRaIwWgWRc6gETIHk\nSa3JX/YIYj0xqLz00NRi7MZSV6Wd8jtgWdJVRstu6ZupcPzBiKzGxGBJT4rL\nlS9HNSJWqvK51Oke7Hl9ysQssfg9vh2dkdrq7MzO9umzYv4E/EREBElqb6iR\n9ry0\r\n=0e9F\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIALwRmlNZjpc2NaTk2Iz7KhOrhe/HU/qu+A+oVQJXH1mAiEAgMdvNCepjdCBz7TVBZ7nxYIYmIUckTf5p7uJ8jWoNpU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.0-canary.89.b36417bbf872b55bafe58bc23bd197658d71566a.0_1614607208395_0.7522497780738369"},"_hasShrinkwrap":false},"2.11.0-canary.89.729ca70d5e76fa1672b3cd21140459928aa61b24.0":{"name":"@sberdevices/assistant-client","version":"2.11.0-canary.89.729ca70d5e76fa1672b3cd21140459928aa61b24.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"729ca70d5e76fa1672b3cd21140459928aa61b24","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    assistant.sendAction({ type: 'some_action_name', payload: { param: 'some' } })\n        .on('data', (data: { type: string; payload: Record<string, unknown> }, clear) => {\n            // здесь обработка данных, переданных от бэкенд\n            clear();\n        })\n        .on('error', (error: { code: number; description: string }, clear) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, возвращает объект с возможностью подписки на ответ бэкенда и обработки ошибки, полученной от бэкенд.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nassistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } })\n  .on('data', ({ payload }, clear) => {\n    // обработка payload.data\n    clear();\n  });\n  .on('error');\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.0-canary.89.729ca70d5e76fa1672b3cd21140459928aa61b24.0","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-63GwBfD5zWe5pLk343CUyeYEHCom5amGuvnFUIRGCeoD8t1Q3x3zbwaoLi7FQ1EgzpcmPD8sGcgvwHIsosdJqA==","shasum":"4e1cfc581ae1f04623d81f387d81371ad222a08b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.0-canary.89.729ca70d5e76fa1672b3cd21140459928aa61b24.0.tgz","fileCount":50,"unpackedSize":909324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgPPOsCRA9TVsSAnZWagAAjD0P/jfK9YA/Dm46FYCM0lQc\n/lg2EHKh9/YkIjJQ27vM//IfUIJXKQGJli5UklM9UI/8E9uwNoxeByr8lbTU\nTaI9MMmzIM50y6qQmM9ZveiMRXtyW9QgbOMEBYiQGIbsPxM3S+Nqt74Chi8K\nR/6lpeyoBs3DZllYvEFzDfZ6UAxDo4jnGDPm33h+aG13NJyrupB9skzR+G5C\nhhShIFQL81fTUNrJg4BhGXn10JqP49LL6aK5PIh4p2FeWTLapQMcRqyumn2k\nOeioByGfsNHVCDODbU/veM+eBgJqaxJAFRnurcCrcEMB/hxfrFe5/VoEyVTh\nvawIwRLzkT02jnQKQkKnd8octfjNi9VGn4CMQcUspSD8Fc7Dli07FlTi6TgC\nWzQa/LBBfz1CiO//3gZyRBGLO1h9qsZTwYQ40bzmlbQH2OwrSlG6ow3gTZpw\nBoMcCQ8ZJBOdLp0NdafyUiEmPvmv13TdgjsasawsNDAhgPL+ZoQ/nhH5lCQ9\nsZe/iCIk8TZUyiadmDIYqSyry5j8/1OpJy6rXGDqY2kQewtBPvCQ0OPyIpDt\nQGVJ4WA0IqUek+ld905PmCsncqeLq4lo5S6jlG1AUrIdkf6woa4+g3tL+L+s\nx6ldFMoAFtnkse1/3pqkOohB1SeTz5IyqVSq9FG85G3glqw75cgslSSJW1Pn\n/5KH\r\n=MTBW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHPwMLtxFOEj31d6vIDC2uhiEvP0PP50oJzWmG4oXPobAiEA0rhGBSkhojCzKH7kQwkYTJrIKnFtU5P1vk7+WVILY+0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.0-canary.89.729ca70d5e76fa1672b3cd21140459928aa61b24.0_1614607276296_0.9143860273252793"},"_hasShrinkwrap":false},"2.11.0-canary.89.16d06df683f8290e083b5e34d2d69deb1a085428.0":{"name":"@sberdevices/assistant-client","version":"2.11.0-canary.89.16d06df683f8290e083b5e34d2d69deb1a085428.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"16d06df683f8290e083b5e34d2d69deb1a085428","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    assistant.sendAction({ type: 'some_action_name', payload: { param: 'some' } })\n        .on('data', (data: { type: string; payload: Record<string, unknown> }, clear) => {\n            // здесь обработка данных, переданных от бэкенд\n            clear();\n        })\n        .on('error', (error: { code: number; description: string }, clear) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, возвращает объект с возможностью подписки на ответ бэкенда и обработки ошибки, полученной от бэкенд.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nassistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } })\n  .on('data', ({ payload }, clear) => {\n    // обработка payload.data\n    clear();\n  });\n  .on('error');\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.0-canary.89.16d06df683f8290e083b5e34d2d69deb1a085428.0","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-+BRg7wF4IoQXf4c6NYqp7oGW7MkeVXurS0OQATZlJUbizwvgkxguorZ30wUtYDH9Cr6Q2UyGSlElZ5ZMedNI0w==","shasum":"224623ab18946061062adaeed446a4e86a086217","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.0-canary.89.16d06df683f8290e083b5e34d2d69deb1a085428.0.tgz","fileCount":50,"unpackedSize":909324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgPPP7CRA9TVsSAnZWagAAaeoP/2zi0bKGguo0Rt04KdGA\nNbBfaYezHaOMXGwgjFRb5Av8TQB0R62pnuUrc1Njx6lP5e8WOz+sSy4ADW/K\nVkOSPfoTLUa8DOmVmbsRnnZR/zxoY81+Jq0UCeze0r2RoBvYx9M9561dM6yw\nb6DcvxWSX+SdrSXgWvyjwHvaFLphl+PSPmX9mu1K2dpk6y1WqA/4tWHlBIqL\nNt0qdI+xfjqJjjGDHXPh/v/ZgX56nc1SfTRn7/0BHvmV864eQIKy+64dY5Rc\n4icwhFWlHGnT+sjYPCrb/rkgtkGNqXPWAvqkPbiJjCt7gNubIDvLPmVjhEh6\nouiPWtm92adUY9R36vozK0TlkBi3SAouyWvE7zE0z2dSmHzT9Xf2fSgdvroZ\ntveQOoIMl5aIUtzU1Nybj+fOV5JPAp+kE/0OHilPVIcwFFrKtUpC/vREbzU4\ndOsOa/a6hdjl9khTTUMuaDgw4E2zuLUkU33J03J+w5WsAlcONmZ+bzERhvUh\n3iQ7U42DwjngCgLfG7lFZ86XboelL6Po9CCK60Ydhx//9coMgnGUkD3RMcTv\ngDfBScZeCJguRCK/RHz8gzL/204JC7JaWu8mn0OtFRikkf7EBUmFyR8ZR/GZ\n6mIyRgSd5dKI5qiN3h1Ah8j6qi5cCP4PgSgHtFc0nUcuuqp8dEceboFecmMp\niHBg\r\n=sIim\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD9IBaSicQQb33MMuWYMYy9zsMgAYM4X/KRYEHVDb1sRQIgVg1357gdEtxzRhA31IVRxx5cHQ7M49M4PA08tpL9Of4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.0-canary.89.16d06df683f8290e083b5e34d2d69deb1a085428.0_1614607354927_0.9253937623688671"},"_hasShrinkwrap":false},"2.10.1-canary.90.008409a59e9ef49eb964feb55c6207bc0bbd4d32.0":{"name":"@sberdevices/assistant-client","version":"2.10.1-canary.90.008409a59e9ef49eb964feb55c6207bc0bbd4d32.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.9.0","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"008409a59e9ef49eb964feb55c6207bc0bbd4d32","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.1-canary.90.008409a59e9ef49eb964feb55c6207bc0bbd4d32.0","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-lU6mJqt5aNrZGvGILljkGQLn+n1WUW+djgNftvTruUjOeUS1NWHde718cXq8O/bWg2QGM3QJpvV1N5RXglb7mA==","shasum":"4ed167ee6a227f059e8c88191d47e941f8165ec0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.1-canary.90.008409a59e9ef49eb964feb55c6207bc0bbd4d32.0.tgz","fileCount":50,"unpackedSize":902005,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgPjd0CRA9TVsSAnZWagAAamwQAKHiAPKK8/C85VFi0JDK\nrcJNOFzp1FB+6zuCOdU7BhkE4qI9JhSvKqYoxP6UDb7VZE2jZrraT6YK8KcK\nvNGcbt2N7T1qv9l7UfcODxU+OdyK7lUc8dQ92i/ivXyZmHV+b/d7djIg+d6H\n6TSsoR4csHKrXfBPaLKIUN5uxm1HfXcfjkv7shDowalRC/scdiWZOdvbiG3o\ntfxOQN99/5btyaHiCYXHJT5o5JO1KsNvXp1a4wGU1soz4DX8SAC/7kbYxL/b\nkLw4UK35yDCeIH9U26zMSnvFrnvTvfSm99TqbmoxGnRanS3W3z4rHPH3U6lz\nG9AyocQBTB+zSbd1YUVkchUC9i8mD9w+W7LGxi2pdrS6/6rSTs5kWqPIGJVg\nedde9EDBI6XWLXzpNXs6dAerjghxMMjKG4myNcYQ7jIntIAaAGZaplx+nQv/\n7NdvZgAnznQYGF7kJDhDlWthQ3mwnTu1wnHTm0+vrS9alFTR0uC7Nv7jLgfy\ns3k6eXmgKqE/ST9Rhm+CR5Wb1FJ7d/KaetVhEV7eFavnvehm2M0ylCahOGSo\nmNuZ+z02DwAAX4loYxD7nkK0RPFrWWs3AROGY0X0NH2pzTSdq6rbU/75Gcoq\nlRgg32ipLVPXNRM8cxkueJSSVTl9Nb6LUuh3einf5xyd27AsrVZB98tI6Lcl\nzTx1\r\n=099E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFEXwN6GNg6QL9S/5TOl2WAgOLDdjtN7x7v+8gSvs7GcAiBje/+pSEwSjlxwwd/tmjh0+Tthp6oFAxpFKsCU311vZQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.1-canary.90.008409a59e9ef49eb964feb55c6207bc0bbd4d32.0_1614690163702_0.6505685866534554"},"_hasShrinkwrap":false},"2.10.1-canary.91.cfc10bbe330aea82a07ea223c212af14fc582c5b.0":{"name":"@sberdevices/assistant-client","version":"2.10.1-canary.91.cfc10bbe330aea82a07ea223c212af14fc582c5b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cfc10bbe330aea82a07ea223c212af14fc582c5b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.1-canary.91.cfc10bbe330aea82a07ea223c212af14fc582c5b.0","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-tMVQNabbx0/BMCZAqVQSUS0/Jsmmj/DpVGoJkCSywcCcbJgNG9rrtG7g80kIx9uvNALHz0sOHwQn4KZ+JgxSyA==","shasum":"c5bf8cd6cbba87dfb6760151c7ed92327d6690ee","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.1-canary.91.cfc10bbe330aea82a07ea223c212af14fc582c5b.0.tgz","fileCount":50,"unpackedSize":902856,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgQKlcCRA9TVsSAnZWagAAc8MQAI2vIDL22VY2NpzQW77P\nlKEbxirR2N+Ki4lnqgXKKjsmRsyVY4zIj9VOKpUyvMxSQ4bLxgcaxYKLZUtl\nEQZDEG8wuo78S4G7gnZxReuE/hytxIMetZQcW8JPEw2ITla3JRGKn/z5uNkC\naiSG9KAOTr5exMUJCEzH+e+fgc4YX0LyHSAX5vE3FNSeFZA1WVUdE6CL4NWE\nAj9SDzWsPNXMBqhGN6uo36kx+ntIFaw1sFVFyOaax6GWREvGrgBPPQN6fnBe\n8Weq7uF8a8u4/xvQrZgvsw5n2QJ3a7WSxKau0hYjZjrlA4nEyq0X3oX/IDQ0\n9V2Yg94ngWeD+EvokXXX1f5yiJjgu6cgrNrpyQEEaf4oRB3HRC8bITCr/Z/g\nL8oUY1mzZikVftiHjsM1UPgB+RzHZFuJQfGt24+JmR9iBcuGR4hcM5nZ03Gj\nJtvmOhJd85o52nr6xLcODld/dgBYfav6irm7JSSyEZffE9YMQdcghEJKsGVZ\nJcv+WjS/DBcsOcBA8c3ZRu1Egiju0lgiaLQMRKM3izwrLvaLuf8Oa3DvpvYb\n7vhimM5kTye/heU9nrgkmsc0qabF1gZRJFKevYtLnAih0JScy94RBrJYN34d\nuugem+1oKtNe/qQrY6UjoIjxgRLI/ohz5gfpa3O/eeYRxE8eJU0x/ySQwAGZ\nTYFJ\r\n=Zu8F\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAyO+4I+ve+vHoRW2YeJt0fDr2EMSDCVRSsAOAEq1ZHDAiEA5FCkWt01doWYzpbQy6ycbwerFbdoTVbtSVUM9rWUV4Q="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.1-canary.91.cfc10bbe330aea82a07ea223c212af14fc582c5b.0_1614850396139_0.5002477906244764"},"_hasShrinkwrap":false},"2.10.1":{"name":"@sberdevices/assistant-client","version":"2.10.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"60ed8249db30a9a56c37ce7e7d5a8db3c508270e","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.1","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-j1GZVTtbFpPHpt9bk7KVFtmihRQ9Bs2FJiwimJ57CWwaAIu9tByIdtMY1cGbmhYj5touEfVhLphYxUISxCcjbg==","shasum":"3f91aa05263388043f1e208fc7e86e40e1b574d8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.1.tgz","fileCount":50,"unpackedSize":903153,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgQK62CRA9TVsSAnZWagAADRoP/3omY5v9UdrOS6gr756U\nVfp2U9sKDXh7JPpXTOHIqtnBB7am3VwG8KPaqX46KbAI9RohRNqBfOLlVXct\n62fR3X5czGC4SJC0e8dUgn0XOYSBVeM0EIt/EjOGXuHNerru+fQ3IxW7D4kF\n8FbmuVzN5rnyYvX5nl1iSjxcfddND7w1/TCFAv1H7pVCVHJuz97N2Z+VA2qa\nnZUZ5e+MHAOWoBSJIPzkHzyGXfjdRrxeqdFaGC6c/VvsJeVejhb/qvsotLp7\ndq2tTOALN8aa6pM71y72lPp+nk7laqiOdVGY9BYows+1BDwwJ0uGVZXdiwHX\nSOlvHEh0lbCVCxzI07RoYVfpBjsBhhjt8lTaj8IgCw8nZeDJiPSOVwjBJQKc\na2YXdIdxkT/9k/TE7u8wi5dquXODK01Wo03bD12sKyJnwbn9S99W9OBtuqDs\nNve00d42knwZPgiY2hC/kBGpwqJeLJQDbCJPS/+zDWsLbcAwZaWiWex1WT70\nqVsqTocZX8NQNgjDxFHkUyhBBMXAK8dmCOpNbiVL50qaZLqPLt+oBV2HQeqi\nOkffbRfrigeu0YqxzF/znwnFiGLw+HMFiyw8O9bjluDH72O86KtpHvLQUS+Y\n6DgGDTR/K9jc+QK7JdGztVnGJo9SX/bf123UBd4TYf6Yg707o80ff7vUXWwK\nqgc6\r\n=T99j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCvwzNaFjrzJE2bJb2O+f8RhBtKZH4HyFscq9dYHVBMXwIhALD03Zxl9s0WwXGPsGsapylo/vr3lX2uMf4vpWIIkb3D"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.1_1614851766286_0.057317029478211046"},"_hasShrinkwrap":false},"2.11.0-canary.89.cfacfb8f79e8a8f8849c1c9bef0b66e08135c331.0":{"name":"@sberdevices/assistant-client","version":"2.11.0-canary.89.cfacfb8f79e8a8f8849c1c9bef0b66e08135c331.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cfacfb8f79e8a8f8849c1c9bef0b66e08135c331","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const clear = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            clear();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nassistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }, clear) => {\n    // обработка payload.data\n    clear();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.0-canary.89.cfacfb8f79e8a8f8849c1c9bef0b66e08135c331.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-O2VjZwLtW48QOvy9ceoXL8J8lFduVQKYg+7qYf/MXWntq0v1vnGAkNttDlzbx3cdKf31bMHhpqMMWhShiIinsg==","shasum":"8253f0fb858d7d8d8a84c5ffcc6f5f1a9070d383","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.0-canary.89.cfacfb8f79e8a8f8849c1c9bef0b66e08135c331.0.tgz","fileCount":50,"unpackedSize":909236,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgSKTyCRA9TVsSAnZWagAAskMP/j08qm58hNEJno70YkOO\noK0MCoXSxm5Vqz1bPE2Tzkp/d3brIp83/bwL41TJIpIcYnlRZ4+8ciczCpzY\nWubXeImEVqrEF0S0InRbznu84/owx1vZr9+QGi2MU82NdiovwBB9a6ucbhTA\nWONYa6oK8DyXMBB2v5mmESlVEx1jwn7aQpsYoG+GpPwkaKJSc1K5iCExGbGG\n/CqulkODHj6yWDO7tqQArTgNIPo+DERLslkd3u/NKWSWQ9MkyK1NmIvG4HtE\nK1q42umG8fRvJtLpyk85tArqlv3KR2p/jTGD3BuWw/0J0slGK3Ogvd5us3Og\nYow2yinvaKXyChV9sit7JRkYKsjvRlAYPB8U71WnJTkgtvlQO9SiX6rpmZpE\nlRjf1ssn2jVRwTif/ADrkxoib3akO+B93L+OvnXDL9z47XPyOmQu6K0Z5dt5\nv2bEFPXazX2AzfXZYup1qAmsziZ0O9od5O8w8EV2gtrt+4geOXrjr6KLGMPK\nOmlX+QjX2g4GBXt5ZM1VbaUoxT6lH6agQx6o1dSaxEx0rWQCTBJ7CtUm+GnF\nQelfe0OeOdFNd2YGTtzrzuA6Ilsk6Mrb92vYs47I/xaztEmrsh91N3GJc5nr\nxL5wXlLAqGGvFfGiEbq6xop5cN5JH9bCpw7IxbR9US4kKCBZt7n/sFWjzbK7\nxweQ\r\n=a+ZQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIARfpsIBlw/AdMWo2KSZqXX/h/uTefaUJLDV/JVxF2rUAiEAwrEAhkLRm/hTdAFTD9yltlM4ACV2+8UaHFvlgBGyEHQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.0-canary.89.cfacfb8f79e8a8f8849c1c9bef0b66e08135c331.0_1615373554247_0.45653015931736185"},"_hasShrinkwrap":false},"2.10.2-canary.94.e2aba4c1fd45236de4a74d9290728f78790087b5.0":{"name":"@sberdevices/assistant-client","version":"2.10.2-canary.94.e2aba4c1fd45236de4a74d9290728f78790087b5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e2aba4c1fd45236de4a74d9290728f78790087b5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.2-canary.94.e2aba4c1fd45236de4a74d9290728f78790087b5.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-HbCROkly8sflqcTX7o7b0xWWChVcNoE3qAV0PNiKRZMctCNUdSa8mouExyHW5ocW6Qp7HDhuYvm4SVcQ7JBG1A==","shasum":"03d682974b664f8498b84d1eeeae6645e4b456ad","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.2-canary.94.e2aba4c1fd45236de4a74d9290728f78790087b5.0.tgz","fileCount":50,"unpackedSize":903734,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgSLt0CRA9TVsSAnZWagAARJQP/i8ansIkoyEbEtzu8oDW\nGd1sJEu2IUWYhzKfJwAdpjoinHHe6bwBWUNxgsMH+RfkpxYqPvZxbiLKvMMu\nsoqAtAzJTL9mQ1t5QXzyf8ocCj3+81U2mFPqbQ10ShK38Y/PAmIki3ZsMYr9\n/ZX29lQOPWGt+MAUPSP4novOrmw+FfQHs0RxyMnpYsSIhmj5hBdETdgqT5uL\n04yXnj5HlhFnIyvSYhoxrbY3mSBr0PTMETA+wU08whgvp/TG90xG7OB7mHrT\nSnWr3XkjnUDHFYwgOFECh9cDPbj2hJCRISNkFW1MlzFxSyrFKeu8KIksUPCJ\nThC75u+w1dcXdUseMa0K/ocVvIVxrZ+cGyOIKLO6QCFG2SdQxX1AMy9Wjr7m\n6t5boNOWYZCocuM3/kVI74eU6RcTEZEVtaSL3mrV/mFMq1ZWuuEoJEi7uZrT\n8H1qGbNCvbEHFoRha5TBXCyiqtRH1Ndh7mkkLmVX1QBvWcs+N6O1TKpUQQBv\n1o4ZBB7WTJVM11a7QuymKdqE6ri0xRyNVhO3eQIHr4mCFqd7q4TTa3jWGG3O\nJmK3FV3cURjsveOayEASHvRAXVn74pW6mleZAUDswI6OIZHnEEdb7dd8d79F\nSgcKGdMWtxGxlVM7wGMjuQoqJ6hzwIw7AIdC+Iu8zV83XQXPnaJhaqCM3wna\nXnHX\r\n=urSS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBtKyLtMKrm6CbJXrmcF/WwCsfBSvoEwBnM+EaoziSKaAiEA7xYhooJ5wRToJRbbFx8qxUSzHGNJrVFBu2Z96ez67jI="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.2-canary.94.e2aba4c1fd45236de4a74d9290728f78790087b5.0_1615379316047_0.8072311476619232"},"_hasShrinkwrap":false},"2.10.2-canary.95.edae68dab6bcc09110684fb6442efd93361c797b.0":{"name":"@sberdevices/assistant-client","version":"2.10.2-canary.95.edae68dab6bcc09110684fb6442efd93361c797b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"edae68dab6bcc09110684fb6442efd93361c797b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.2-canary.95.edae68dab6bcc09110684fb6442efd93361c797b.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-Zr386xVm5Fy7sNdhSi7lHMknvkU/I9IljmlLXnTqzGYMST01DeffQ0kmcLDVbmMlYmxsrdmSMFfyCMLohGLbtA==","shasum":"a276e55758bdb1fd701babd3e95fabc8a25dd520","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.2-canary.95.edae68dab6bcc09110684fb6442efd93361c797b.0.tgz","fileCount":50,"unpackedSize":903492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgSezLCRA9TVsSAnZWagAAwiQP/A8b3YZxKU49tPwpZ9NV\nkacdr6XcBbxqrayFF0SgKhx98YB3LozWNt9IPxkco+gS7Qvcxwv9slJu+5nX\nj9+ZbYM2QLKBlgkd3zqpAB+7b5lGMPC1Upho5L5qcCXzzIHDD2iOEyaQ/qmz\nl2s2rFI3/rQTXQOsRqG4G3zA8rW/JL5FCBemiEgzrS9kevHDWMwgtZ+KeP/2\n6SRsHBRDWP6eZ2MDZsRejX2OYe7O+a82e8AloT5mgxqVainqJMRapsT0J3l/\ns/OSmjQP2Sx5SW2IiQjoCCVRnMwLKCWQUy4lXP56L+kygUEXDAKmf2e8slWC\nm+N9PrhRbNLQExCr3MYuQtUBUXvDJW75UeTe+RboV8Fk66tA4cuuQLYGwKG5\n0SWkxMdmi/BBcE5E3ZgBGcbwqQlYa2KADNhZ+TosPmy8hpbuV50BO+TExCdm\nxggDfJESoJcQ+mIzhxALSPld65VccgixKYJhYqEHf5day1HGZulUWm2s+EGe\nP8q++44gNt/Ew9/XSAdkXXEUatQjCXxn/Abqhg5CBAA1b7AuuVls6/rPhkDQ\nnCWi10Qf1sVyJJYLOBA5ZLTpNOTNE0GJAXQA/0NTR7wfSfWblZYwmKnY7xV8\ne2IV6fMbz3D6cdjB8JYyC5z1h7QgUXwE8KFwBgt/UbuTg5fLPk0IFVPBMpoT\nvrPj\r\n=J67E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD1Cfn3biMGB2KfwZb/SItmGb0Kv3dXYzgCVjEFXm2wzQIgIdBZEwOe+j4/ANwpSqiikSMOzzmp7cjz493b9NuODrc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.2-canary.95.edae68dab6bcc09110684fb6442efd93361c797b.0_1615457482718_0.9327795022960321"},"_hasShrinkwrap":false},"2.10.2-canary.95.c4a4d28bef2a4aacf3a3669be9675e66f99582e5.0":{"name":"@sberdevices/assistant-client","version":"2.10.2-canary.95.c4a4d28bef2a4aacf3a3669be9675e66f99582e5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c4a4d28bef2a4aacf3a3669be9675e66f99582e5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.2-canary.95.c4a4d28bef2a4aacf3a3669be9675e66f99582e5.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-Ap42O6zB/+kUD3rSAeyGoRHBGkjlducdBGwztjEjngkefkIjZKNyXG6D6pb9gVWrYLylifAJ9t/aT1BhptiopA==","shasum":"f671b11ceacc48e046e0c58872e8608ed073e76b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.2-canary.95.c4a4d28bef2a4aacf3a3669be9675e66f99582e5.0.tgz","fileCount":50,"unpackedSize":903628,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgSfH4CRA9TVsSAnZWagAAr70P/0lOiOU7f86fSfvjPr+d\ntj/Ovczld72TbaWk4oQqz7i17CVeWmlzQnkkkrMKB2wlOusd36bzuzhO1v9W\nvDUheJwK9ypT3HFiUcWOr/xXBKsBnfSowRhgSjMQM985x8aIagV97gKW04PJ\nIPczT6K++s2wICMJPn6VJX4G4iotMnqxlMVU9eh47ico/j3dT6kzPro1c08e\ntH9cVX7SroG1106lj1RzBu5VQHpE8VPE278kIfUbo4QXpB1vkaLfSvUG9aXZ\nLJZyn/2ErFLhNXnzuYPIdevK1GHSuEJPyQwGd0l9h/Gzj5oKIdHHBdDzfNQh\nnmks0pMnqdDH5320wvtlE49LcQvq2rKRgKpAzlTS9p47YZPFeIBilJhlTbTr\nggGsSlfXRj/qKTaeSshaZo41ZO6NPbQcdSBSQFVruG43ZrCxvESXwx7GPmRk\nEy3zPyQXvC9fKGWqRBbSX8G52Imw/TiXIdW4nJg/CM0yZp60475lrLtQT9Fs\nDfPP+9/Q5mUreOyY7lUqQVKqa5jA1QBbol1huSPycJrFtEE7hdLsOSjDBZb1\nWsIyndsNSnCk0n0hG9ySXXqaoFnLZbr2JlRLMhci8UTD+PJudSAbzWT0pIJs\nDHC1BeZokQAk9asR7Kvc4QaffrFZZXzUb2Qi+LXgSO1ES5R4n/IJy8K1o1it\n4HUK\r\n=PgeL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAMReugVyUk1bxETYMBjNwaIXiQ9Q8XupKCFJ9tQ3fEVAiEA795scnl3D8Vje9hymkTFP/YbdMPOGaUxiaUmHrXCHdM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.2-canary.95.c4a4d28bef2a4aacf3a3669be9675e66f99582e5.0_1615458808069_0.8865935248481347"},"_hasShrinkwrap":false},"2.10.2":{"name":"@sberdevices/assistant-client","version":"2.10.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8dd260aa8f717fbd14df5da531e9c037aa17f838","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.2","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-KDKs7rqZvB27lk3gCC/DtyvNW9AZe48m2RJ972Fy50Tk/XyXPNuAeUzcdyXoL6QbBXvr3QGOPG4sUBLpqk7YHQ==","shasum":"2caed46f10c8c02ba82b1ee3c51be5b08b1f4773","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.2.tgz","fileCount":50,"unpackedSize":903928,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgS11QCRA9TVsSAnZWagAAIV0P/2h6aUImI+gNnszEhDZE\n2NUsRffdIcaI8o0durE6gHGRg8/rm9bAIN1KUiX7O/X8LG+990fZkNxa51zP\noGli0C5WsbW/Dd+NhGv5NiSQ8Q8NHE+XK81NIKEO6ztVk7BdsNfupSrOVFkz\nNuMfMcS8Tu1Bior6Tk7mL7hcqW5oItYKGICrPUPiBk03oH/730NzIHKtu6nB\nMvxSSO8WaV9C+RxNP0FBs1QcNQGyk7XmgWVFeJhRUgvix4QtpR26bnuDbGCl\nLVJAdUAtkxV0XMpVwcyuA74vjFArz+B1McO+/e4/E0XDGByKcwoL5fefbKir\nPiVQkIfg5SaYvGmDWqc8JXAIRE4IbATlaEgAK5pMPc7cbLgru7bkvktu2UaK\nHucdLMLeCsmeMaiHCBshaqNyazObbU0pv/tEPReuZpdkb0mz60NRrB0r04I9\n1B+qiLkLS5ROC01/vDEh7Q3zDjeM72of63rjH1ItZSZWCcrlxu4Zt8F08QOw\ntgAjkigz8l50BYoTvrsHMZsPH58x7pkzXptcjTG59izvYTb30cXCOFIqr+Cq\nWqhiCo+j+CoKbWVTl1v1CjQSKnbd/XVTewUoF1pJo4gCQbvPRBp9XwK7fkSx\nL+91rD2d8moT6aEepYmdNBbSDhUdyMrUNANCvkzZ6/KviLBgCAmGz9rH7u5Z\nyj6d\r\n=JZhL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC8XQ38ljp4W0GGCnc3fNqFYZj3f47Pp196x1ANQkxjdQIhAJJ0pKgPAzGEyeaqxYPfjMpMOS+mvLFIdn45ENlIAM4j"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.2_1615551823772_0.15268144195874944"},"_hasShrinkwrap":false},"2.11.0-canary.89.74a3bf7281113c9de2365bc5307ecc768e0f5167.0":{"name":"@sberdevices/assistant-client","version":"2.11.0-canary.89.74a3bf7281113c9de2365bc5307ecc768e0f5167.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"74a3bf7281113c9de2365bc5307ecc768e0f5167","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.0-canary.89.74a3bf7281113c9de2365bc5307ecc768e0f5167.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-ZOI93jtdtSpNjwIGIXjfmWTrDLr4Id5pTWTiC+Aj7W4TVhxY8jyeo64NYWjXz3anz9k12WpB/zZ0KRVaPBvvww==","shasum":"923efa3833ce6ae1a72a4ee324a42cf648446b84","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.0-canary.89.74a3bf7281113c9de2365bc5307ecc768e0f5167.0.tgz","fileCount":50,"unpackedSize":910042,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgS2dmCRA9TVsSAnZWagAAkrIP/2XQOUwYumX7ZAMXgM6Q\n3ulJx0rG0K0X2Un4EEjpbJvfmiwHPK5HobYv77ETu/BQZq4DF9mesI2hBlYJ\n5/cL6e8sslrdaUPrJTrYIAl1gcEa0bHyYG4kY+phGvuxNmysFJnz5vwGGyuR\nTAeveUR6+/mvB75qAuSUMWBna6FfVdDTJ1Q82wBATubQilOUf328UpalDiTo\n7DjLI64ps88s17DzRaQyA8qA2UXTsO2zR6d2pmOzjhLsZC3Y2bpTpz5BqW6b\nYTU+vRQdFsz5PSwSeuen+07atk55yycMo1vKbhIf8G4wLKT3/9tnUP1XLuP3\nyUsk2gzhLqiFYj1+cGOYujdR3wGgdN/zDYgIR2AJaotIZU2nYKKId7KJ7S81\nZp+6+DF8YPfnoZK9QJma6nhiz9k+AzNvgQJ3TX1mYCoOlHr/zIfTYOmVNE4j\nPNOzagxitAbh7vdq27f8osWEQlI6KhScqkNkQnS0FqBLLS9HoN1HKDS91mW2\nCCHdxlNnreUKiWa+Ao32gvWv6dr9IV5zDdX2/RwLzsNxgfYDCoYtwgXywBiM\n5b77GSVxQ1Opds+8UZ8LnXHYDhWpi25Jen/AziBQhXRP+NXQHeYapbsGoNu8\nriVzNIes4cDbQi24fGKGioag6CSvZyxe1GXNf7EMkpUbQOY2sdYQ7LBC2bPW\nq6ub\r\n=304W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBdAwyoRoGlmI6iDoohG+DqyYSVvz+YKN8UpHgi13geOAiEA7ClmBhtReS4Wm1GMZ8ouAFkScby2fNPrr5RSNQ59jV8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.0-canary.89.74a3bf7281113c9de2365bc5307ecc768e0f5167.0_1615554405427_0.44138213053056563"},"_hasShrinkwrap":false},"2.10.3-canary.95.a827465636976d0d4bc61a8bd356048f4186b1cb.0":{"name":"@sberdevices/assistant-client","version":"2.10.3-canary.95.a827465636976d0d4bc61a8bd356048f4186b1cb.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a827465636976d0d4bc61a8bd356048f4186b1cb","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name' } });\n};\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, any>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  // Любые данные, которые нужны смартапу\n  smart_app_data: Record<string, any>;\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным action_id, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.3-canary.95.a827465636976d0d4bc61a8bd356048f4186b1cb.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-uC9Du1BkEtxue1WPXq1XVTtuTMajVE5pM+VPeryjlvkADr8SAXUIiVdYscUe8TLuA2XYHZSgTUB4f2Sg3F8Hbg==","shasum":"5112fb8d4ec5506571260252ea0698970a58050e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.3-canary.95.a827465636976d0d4bc61a8bd356048f4186b1cb.0.tgz","fileCount":50,"unpackedSize":904423,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgS2f6CRA9TVsSAnZWagAAlBYP/1xKj2jvInKDiHQiUV1h\nQ+ZFeOZoMhOhkttqKUHZdjc+QtfGuIG5UOUijXTqZlTAWQH3Uxcl3gUTFOzS\nG1Q3wFr+j8WnyQbwXM/7lISxnlyLmi1EO85hMfj6Vs2mAuYKQ+D3k2kIq9X9\nDPT0mCBbKegzyYEOMqhX+Tq8y728GWqsP48MlyaveVJXAePTbMANj1jUjQvd\nH9pCyCxJ8KIeFHfs/LNKt7R6v/6WLh0xT6W0xen2oF2G19aooY9uxwEPdapG\n19+JGOWb+flQXBD7gphSFTSpO/TTG4FHo6DGou4j8dIwuZS0bq/O+WWYG+yP\nUHLltMeOR6CB1aHdaoc6EnO1I/WoENO2v+HfOv2UxFKCA89U3DiFLcVdB+DG\n9lR/Z1/aKhzNTidTmSPL5Ttcm4xFIUeNk0e0vLM+CmOQFk5FxMnlmX8HWaoo\nTzV2M/hldIScZthXMudTB+j/1VWhZj3iswRDPrjV2FRbkgJdETA5yLlUKe1X\nHzHgPer1I5soQ5jh+Z8RS8LnJsYji5f4tGXz8o/63GBjKttkDDdtiULskaZB\nA6HnLSvrHHwow/bLoDuyzSRBucXBwciccHpFsRjjHVQDCRw14sEo6uRFTkCM\nLAAKozxmfyoJJtoTuTczLDKfgPLdRJDce1SCRnoJxpcJS70qUoWPz/br3Qxj\ncK5b\r\n=1DWH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGfMqrsgtbk46ggP9fsXbbBvkHP8iHci+laA5o3KAQl5AiBtLgCadIIs5wfNUcIqqNGP7aoVWYnktOGp9Gi5NWMEsw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.3-canary.95.a827465636976d0d4bc61a8bd356048f4186b1cb.0_1615554553218_0.4781493270396162"},"_hasShrinkwrap":false},"2.10.3":{"name":"@sberdevices/assistant-client","version":"2.10.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx .","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"76aaeb042accb554912620a167d334fc6cb1eeda","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.10.3","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-PhKeyKleNAbzkNzFv5FM5EZVrTiUPsDlDQOTGPkEqgZXw2+kBlS7jEWOdgqolfohjvZaD+CthRMcTkCOedtTdQ==","shasum":"1f0c233ce0df9e368b30181f3260be96a38de07d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.10.3.tgz","fileCount":50,"unpackedSize":904815,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgS2miCRA9TVsSAnZWagAAJU4QAIODxE8pqZt8OLNTZGKV\nuODTQF/YUpQstgurblY5GGZnuXTZGa+mGJyjTI6LzGSSHC6Q4c0Kq/az8LUj\nuKB0lNWN1hYAtFtVCTSmJBavbfuMeV+Ogjg9UoC6ynmjaZxwkwnwW/IsnWHv\n3k4j345WjSUfQLIBPxF98ZstJxPZ1wY2qHzi0pWUdnrGNtq7zPoZiZ4YgYNi\nVIpnBckMfqIUysVGlMGTVMoTwwS0fwF0gCI7mSyMkpS5YLmLGqGFXLGHK2Rv\n9gUNHLNIfzJvsvuNQbGybiLG+1M53wtLx9elfWMu+N9aV+zPEVpHji2o6rHd\nGRxtULVKorMRM09ujR+tiK/ThJZ0TPi5H4RjSdugDNUeacdd404ewXi4Y3sh\nAa07oY2k8HHI4F+NhQaCP3ZJtDfGDa9/g963y3UXvaW6fx/BKTyEI5b45iW/\nlLteNdiWTFoComx4bnGs9ZmRbgIHFk7H+2iJNohkHeF7ZY6RSnSlBSWO8En9\nZzYNSpXfXRQohFm7MVG0V5qrtU3GqmwxuV8kc1mK/XghS3H8KLGTTfzXAlxy\nQDB8hWRnImjQASMeKRWYsN5mSNq3EQhX/iBU8+jUwnuFlDgbQd1cl2UDHspZ\nsQCvfm0rJ6c7DnPMlQ8gM85JigKVXFuh61i+mSFnIaccri/isF9dSnBgX295\niRSG\r\n=oHqK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBebPU24/tLw5Q/QI3fyofRXBjZFd0hiAez7QGWnwgD6AiBWEi6TjlECILCwytZdnrHe3DlBe57aFQkPAI+3LqAvNw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.10.3_1615554977703_0.2760354889661676"},"_hasShrinkwrap":false},"2.11.0":{"name":"@sberdevices/assistant-client","version":"2.11.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"095a9140693a9a5dce613228ee2b4e45e192b257","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-UGWxDcMqkiVzFJGFLgcuqMO3jR7fF3W3rZuwq3OQVz3PZKtB4uM8tKQPg7IMZIz6+CVSYGFvn4RFj7O3fbyCcg==","shasum":"44e49a5c597a1c2c61f1611381ff1316482e0241","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.0.tgz","fileCount":50,"unpackedSize":911379,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTxY+CRA9TVsSAnZWagAAH5kP/30yooe7snYOZVGBKM6E\najp9IQpUVyl4x/B1bGXtKAHuk2ZooOi+72rNVUbkNccccVOEJJmKfdstA0BQ\nyk6lgzKEgedmNMUzEeqWxdfSS2oiScuMRrOEf3Jez7f8UN975i0NcLepSMii\nNJQ+YFjPrTeSTmTkg8WBvmAbOyuLRWXORJLmOmG2K/5k60fCO90XfvWuLsob\nmwSwUzNoOw+jz4xEoAg4Vj6dHtm4W1+A8x4sBIMZfCgs9l9ORePQSdVUvqwe\nviEIJHeV+zWq7DtfrceM5u/p6iLsjB8mmQvWWtHPSoW4kZH7FETtg35cCMjl\nNGZqBqjWZtHSsLVDlR3xCT2O0f9ooAdiSfcH6dr3v44WpXjk1f+wWdM0gUBe\n5PSlMPuJbu9CJpFcxdHy4Lpml7b9KAqrbzT6c04obs49pGAHQ2pJTcMBIiYl\ntUNcYJ8EQoTmebYhZMZw8g5ITbQYkF+QFfjm0rZbs85n8L8YP9RUprgvV9Y6\n6gWbPmD8mtokabxyfXzxjh5tXvZc+C9s1GVlvtHjS8CBWcXMUdkUGYChTQM8\ngTGs2yYnNasE5myHdDqWMECBIr/NbERybW0ZDBUaS1qPusf+eFbyDDloh7wj\nAAsE2EHIf7SNpW/XrU+0jFJej3zezJiNXQgGsM11YmZVDYfO0yZ+J1yo5PJw\nhf9d\r\n=xZv6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEicRCwi3CC/zXOWhLwhpyQJ/QXUUBiORHjZ2PERP1GrAiEAi686r6Q5jPCLp4oo/GIOYNyGQ+qNL+ZSHt3/YAG0j5Y="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.0_1615795773819_0.17964367535976633"},"_hasShrinkwrap":false},"2.11.1-canary.96.c2d5e659995d90bbec3ad6edd1a43757f0b52931.0":{"name":"@sberdevices/assistant-client","version":"2.11.1-canary.96.c2d5e659995d90bbec3ad6edd1a43757f0b52931.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c2d5e659995d90bbec3ad6edd1a43757f0b52931","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.1-canary.96.c2d5e659995d90bbec3ad6edd1a43757f0b52931.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-cGhjsC3QX2c+8JvdC4C47oBudCig48/M+l3Uv4NN5+rgpG3Wljv4MKsYf0sdF6bY1u8U/uN6UVVVjE1V1CGduQ==","shasum":"b33e8465a5efa9aad516427b42e836793b4da791","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.1-canary.96.c2d5e659995d90bbec3ad6edd1a43757f0b52931.0.tgz","fileCount":50,"unpackedSize":911680,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgT5QvCRA9TVsSAnZWagAAM8oP/0wf04hNA8Ly+DXKFP3p\nyDqGOyexhFoJPQMMH1dyKZQklMKuMCdZqfCThsdZUdY9ItS5m737rKuDUTfx\naryf/tZ5a45WOiGLvHRTyGiRjoqcPQgIyBqFyKqjmI/q/ZhARzaHkFAJXKRJ\nXAJ+8SWV8HVHdfptxbmegONc9QcNJR3tnqHmiSeE7KSC43gZvsyiy2Q83l0F\nH7WBUXKJKCvv2/C9ngErYovxOO69t2dg11TvYBak7NtJAVwgUGRYMLKv17F4\niMOX6DM01gsUzhMRveksiywrV0EW51vzj0W0Jipn1rWtc5PubhBjLBnXbBNr\nR5EY+MitTJkXtVK6DSHOe+KLEpKba55xl0rE67VtlLAl5a2jNoRBphFui2pz\nB6T8fRakebo5BEZyC/v9thSjbt8pkt6TSZsKTfg1lUCu2+mpSfLQu6tk9TE2\nAsNQcGANMfyRwP4UIuaRrt/zRoK2Lp6MiC/DveDrHKvn1aumsjDWDEDoCTBm\nyoLy8KuSec9b2RTACxUPC3NfQa2MWTiP6/eSZcmetCUcQ5tcd0K23GfzhxFt\nuSdlglsQ9Yoz64yYTm8Xq4sOmv6OadpOl8HmhKU/TjFRs+yJ3SniArobR5QB\nsxv2oDnz0HADWu1aMb3UA7yxT+q3DvL5G/93+NZ784ilnypLow2qryI4UaLI\nuiIt\r\n=cRs/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCYBi0daUwAuGDwvAX6EqC59OgN1/I3YXsIZ5Hn0NrBmgIhAIPpFfeWAY7T7/RYsDkHmjOkJ0g+6gWRAQYWnyQwy2gz"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.1-canary.96.c2d5e659995d90bbec3ad6edd1a43757f0b52931.0_1615828015230_0.7024025573891666"},"_hasShrinkwrap":false},"2.11.1":{"name":"@sberdevices/assistant-client","version":"2.11.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a668abb84b274e8091a7128321255ee1369ad06a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.1","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-orXNTK8OhcE0M0oXDXpQTcQS2k+Mdcd99g1xdnSVAYlrFJ0WzIur52xk4Ab9YIqfSh3pDp7m6WYRnsR/8kb/Cw==","shasum":"23bf0560a5ec71a034aa9f7f0de34ab49c71082d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.1.tgz","fileCount":50,"unpackedSize":911864,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgT5erCRA9TVsSAnZWagAA8wQQAICrVytq3cTi6/7TrBNV\nTBGHcsi2WO618rCp+DRspl5e/BO299YqeNQqsH3LuXIBYYSOmoOEReUIkf1k\nwUxut/1afITgahtV4/wdz+AfMaP1nflf1AH6gd31TMfg9F8npZmhSKuPcEbe\nVudZaRwudmsM+j2ipXFlueKb0l+yhsD0Tdd2W1R4utGX9WZTmsYEAZU9SDWb\nxOwv2EICdAOztmwajep4xdw4JcWZ+ykLQCFr9YP/MF0hQgOkwub5txZKjZTZ\nDzPSlW8iteTh7t1NaP6BLFIPO9eiuXa6WybfF+RXsa4+9ISYEZ+haCq/NOV2\nJk26kgfhGIx8cYsQGgwQI2bwjk1+WxzcLIaDrNBbDZUkV3ZCzw+ZXbWFkkAR\nVPSJ1DkfvHw+lhGMUP4ocSy/RK35wfLLheaq0WkOZ4fawTz69Y3fqHP5WUfC\n/sNmalsDOBewapvOD4Jmn7orC2Z8b3cPmYe7MpDmYkFANRlx2vFHdxtwTqHs\n7K92AaBtkTUu+OMa9Pf1At+5ptZArwIjzUYosy3Ltfe5BVxJZ5YLtkxWkzBp\nsnQmXZVQAbSY3/4EZ5H1yuqz9hUe0RbORlFd0LtLxCwcL7LuD2Y7hdqXwImM\nHg03PjZzQxKhRiiy5D1G2JhytAn52Es3jLvT2G4g9qHLD9l7sNEBFU0rt+Tr\nzg+3\r\n=5/5k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTenX6qtBLAiPSCo19uvx+POCaYUAh//UqRAMoVw2woAIgQngJfyEjQwFXwTTGK3mvK7rU7A7RZu6nDBoiZdyq8b8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.1_1615828906380_0.7760129600094372"},"_hasShrinkwrap":false},"2.11.2-canary.97.987759fc44c8f795e4220393b16666ce743e6af6.0":{"name":"@sberdevices/assistant-client","version":"2.11.2-canary.97.987759fc44c8f795e4220393b16666ce743e6af6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"987759fc44c8f795e4220393b16666ce743e6af6","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.2-canary.97.987759fc44c8f795e4220393b16666ce743e6af6.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-QPHX0ZoAVj8kTW+KSwiPl0YCdRJCKWq3ztDqycKVCQCgIsqdGQYBHEDoWyqI6ehpL42Pn91Cm5QiT8FUvANBgA==","shasum":"e52e4eae1d7c934e9031392a33675698101ecdf3","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.2-canary.97.987759fc44c8f795e4220393b16666ce743e6af6.0.tgz","fileCount":50,"unpackedSize":912077,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZYMpCRA9TVsSAnZWagAACp0QAISyvZcRYqwHrrXT+Fx3\nGogsntURLCCmF06lz/l9YYC5Iv0VbTWKTdXLuuSUaEg5QE8vf07lySqHOogr\n7klKXc4IXQopqmVVmkSHCD9pPAV8LgKUz117wYB1PtZBoxUqRoERLb6T0ItY\nf/FhWrHO9zpK5KnYVsTt8O48ywbMn6JD9dbE6RpX7TkYMPpwpSOd239cGKml\nyGcaNPPpitzBt8wI+uOc2Oo8+85jLNbE3yB9dCSDwS038u0rnbY0BJQDmWLU\n2TEsgNiew4SfLH+ViDG2FX3UD5eCU08DGfeZTMxB6i7g14ifSxPRtv2YidrG\nut46pu4o3yc3AXP/t3jECTc5Qzx2KXQtPZOHlOsPLzIj3XapzQNq3rnp8B6R\nB6MGF96JkbT+iLp7KznhcWdGlb/WDqrn3u7miINcRfhYpIg99RXKGfs+YeI7\n6YCF+4lYg8T+dCD7U5DYM3sQWoWSEH55GijM+jZJQlE1khljz5nvSq/mn+PK\nljH4I16L9vkT2AxDmHxNT18TsWjCpMLKzAOt8JHU7bTVuDNSUEZPorP9Zsf7\neNO1tfqo28pRY/RJ4Dss1sRuiuePwWI6a6E5bEGzbIATHKsPLTHT3SvLAgzR\nLIwx6/f/bj8gY0o5D/2SHwY6kUkQJ8czG8hbRh3nGEaSfVBTGWvafWy/Fqls\nr+br\r\n=3zZ2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICGOZFYhJVVzhmTORb4tcAN8Ps3W42KO+YZat4U4DgL+AiBYObGOeBR0lEZh7/VoxEENULToM5+iOOpJFzd+HoD1Tw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.2-canary.97.987759fc44c8f795e4220393b16666ce743e6af6.0_1617265448662_0.6762043368278681"},"_hasShrinkwrap":false},"2.11.2":{"name":"@sberdevices/assistant-client","version":"2.11.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7ffb69a9e672b5e0a1041363a5a21a1ca07ba50b","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.2","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-xb3MX3HFkAzezCjCmJd8RvQpZ4fpbjwxTIySsXsr++makroTpnJ80x9uRmVq7Gdi4gMI+fb0UOmCQEg5HuEhzA==","shasum":"e02f78537d75e06b34efd0d083719a9eabdde998","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.2.tgz","fileCount":50,"unpackedSize":912292,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZYRCCRA9TVsSAnZWagAAYaQQAIb337c4p7QR+iBFYXE2\n+Ym7foKsMSNHZxOL7bwxOLjs7MWe8AA+DumFj1/UTzb7kJPlGv8B8Ndb5e5N\nbs9HDfZDeAPJk0SMqFiMeg9FNO8+oIqUNs/CR4/8XgYBq6UZcoil0jFw1Xho\nwlc62xgTbFwY43Qctp01bVPj6xVc3/dXHJVOibkejE37lRXzNNM/je/vkvEe\nJUaU0LdJiRVTVLrNfV/aPDkeyDqUpiXQ7SoG6VdeuTA7SSvH2vj5NatNadpF\nFq0dAOyciQvh2y1T1XSWC5kSjxSlZaD3DD29ZZJlWR1TUOZYp5IvZHJv+42w\nME5YVdUYqhKwnsodsbT6/g29cW20UtCpmzldyNKWdupgW44DcAv//PQPbmiX\nz/FKNQnLoiimTqlbjX4gzI4sswirPyU38H4JOZtYgKKUaZ8eeU9/e4x5+TZN\n+vGMnwZkI77GR9Yiax+0apCLMNUeteMizBpl6hvj479HLKDWFMedarrawdBC\n44RhUNdPUbfVzyIcScxYmxD9fFKQwVT6zVEzCAsbnPYb3Cljxouryfcm3Xii\n74qm1kQGuimQSbY4Fp4x/CiFoaBrZdQdD+JCzy2NHBTS1ryXVgoAULNtHcaj\n5YhXHzxFHE6OScimumD1FGM9ML8lCGPnCfNaGpStbULlbMTNV3Pox+JZ3T7d\nzmAb\r\n=tpw7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2dTrIOyoyrM/jcECZbPfpamHjdbs3g3/9z7bkNm4EGQIgShbLzP20UKgKUqD3oL1ZPDkdjTsjLzbAYJ+GV8qRsU0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.2_1617265729813_0.10590333577757916"},"_hasShrinkwrap":false},"2.11.3-canary.98.d553dbb9a97243f506448692fd9a252f7ec76729.0":{"name":"@sberdevices/assistant-client","version":"2.11.3-canary.98.d553dbb9a97243f506448692fd9a252f7ec76729.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d553dbb9a97243f506448692fd9a252f7ec76729","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.3-canary.98.d553dbb9a97243f506448692fd9a252f7ec76729.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-iOB0Hi7p1luPzl6B+ubw+NaONPPTnvbPph/rWQvvCAP91xtNu3oBS29rzfVHjSQI9wJvGlSfC8OIS7gNDYN3wA==","shasum":"4eb4ea95feebeb86ab9836fe8b89a16da880069a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.3-canary.98.d553dbb9a97243f506448692fd9a252f7ec76729.0.tgz","fileCount":50,"unpackedSize":912509,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZswOCRA9TVsSAnZWagAA8U8P+wQ7efSLb/Vc3i5zQOm3\nthW27L4qqGwq9Johm0nfS26T0yAxF/88f0XYiAeJkkjl8AVDc5jwC6REl7Cj\ncSOQNu26yzjJ918JDFWyCTvyY2z9gHsdZX+31b5pVmZJuJP1uMSi7PZhf5r6\nR3VJ8gazJ4eEpJZMeECH+SMsYifj+pXLuMg04NNZA/uJtT07Fyj56fVH0twe\nLBVq61rXU58HprEcV7IOeCuRZt2rhf303+ncchZY05Jxm4Hn4vlVYBZgA3Kk\ngOU0BD7arAZtumVMMXx7oG8CDTctaaFUYlJlpw1JsQg6pxtmnigveRoZ4JUT\nXGPzW7it3bkGgbUVTAeCfahQ06vFwVaxkA9NKQhf7nxgMsqqLA85S8AatLYp\nfLhVg+8oLgau7Qaor2Zg5Sb0/5nWo5bRl2DYvVQrhMzHwFHZ1MZDwU4id2Fx\nzUL0y/vAyukukM//bglg0Szbwfx6Xnw8hrsOedwD6ajMEMzG8ANewa9BjAhC\nnQhxTwAqfHHNDidcanc0D6QN6NZk4q2S0z9q4+V7VeU3A7BsaEyOUCcZWIPy\n15mD4NozIb9Tiu0Mb+5J8pjH7zKjOp1ffAPOayl6tNBIToTxFlqBHtjg9XZP\nsHWl9KPkLPtFwklbXGfFUwSfRIo+czd8srsI5yLvA8k+3YmXJDVdB+yxkvyr\nACNA\r\n=PN9n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC+8RtrGt3x4RX8vj1oWUDW9CWokC3jxNDqq1pEaaqKAwIhALPFsVS72VflbmWSc3slYcmtl/t15Xc+5MfFzcD1ztTO"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.3-canary.98.d553dbb9a97243f506448692fd9a252f7ec76729.0_1617349645507_0.6437474853806036"},"_hasShrinkwrap":false},"2.11.3-canary.98.92419efcaab792386427c3d98caec628738fe29a.0":{"name":"@sberdevices/assistant-client","version":"2.11.3-canary.98.92419efcaab792386427c3d98caec628738fe29a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"92419efcaab792386427c3d98caec628738fe29a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.3-canary.98.92419efcaab792386427c3d98caec628738fe29a.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-otAVIu8lHiIti1oyNvl+6SOb2dSI6NsAyHe4IK8sH3O28r8OEgM4YLoDfraq+/VMewGh+D+otvmdoSbyp1AoXQ==","shasum":"f02cbf8f08ca54e8ac104d344747c6adff5932e4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.3-canary.98.92419efcaab792386427c3d98caec628738fe29a.0.tgz","fileCount":50,"unpackedSize":912509,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZsxJCRA9TVsSAnZWagAAvYgP/0op6i7LlQ3zk2JJDZrH\nwSI0EQ7DYfJeBp5fpEpYfnR6BnRhBZoaoRPuMQB/7soWjN03ZjnuEtqjZftL\nhrnhL7+ryaiT/w54MXopRYhjBNz2bOd/B8RzZ/HhFnN0xVvpiJN6D/F5fDlu\niPmvgXI/L6VH+bhsWTz4R78iPxOeg5DaFgwOGaaeWwkHIRVWxCzmONY3aOh1\nuOLv95biL1d52o5tgnMDgKGLFmCwSlpFb0YgJph3m7InYLQL+KBakQu4pEau\nirPH5yV9MwF3+R/ayvX7H0UhkLY+ATFI1VN7vJoxUVfCucnq62GHlxVr9kw6\nRPd3a1b03W4pu08pvGfRuQJQaB6ygQpXAukf5/1sE5uUcgVQAblnbEUtJn4/\nfQL9H45/AGX0k5JFPAb9dZEdeC8CYjIRcWTIYFeOc59b3Qwz1dWvpbYzBq0L\nDTmDo+RP0CsNuKB0L1k9mMO307xvMnl5nUCI6HnWh8pXwmSd0xc7AVZtI5ua\nJUVKFFCNUd+3nx31X8tp6YO/s1i2cT1R5P1SW/r5hIzmWBxYcbTiPSvW1pCZ\nJQ5NYm9nEHoRBF68Gb19o2qyHR/A/g96c498hNdTbzW2eRtdVl5Jk1bE1pVH\nY62pLwi0OpmuKtahavnThv5nXE49xdNTZqyO6Og1ELurDMSbJ9WIlHFiDqLV\nNuc0\r\n=Of/u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDERkQkP6bYFy4+1G4sC+raIheMxk17vB9MjkOfESBJMQIgJW63SRAG79Ap6jtfdKHrFiW+R4CbwDVtHPMkV5aNx/Q="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.3-canary.98.92419efcaab792386427c3d98caec628738fe29a.0_1617349704621_0.0817919419258002"},"_hasShrinkwrap":false},"2.11.3":{"name":"@sberdevices/assistant-client","version":"2.11.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4b0fea5d68689ad1f9aabcbd48d1f9fb94bea252","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.3","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-fZ/bE/jtoWWwX/DJlONyz6rWWr4EHwyvpYu3pVRvgmlcRdJXjVs29Rv1Ms3YXRqOXgiuRSa5ISRuRqrCP7CIag==","shasum":"11e2864a354df7fd530ea68c799c15a2e9ac3541","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.3.tgz","fileCount":50,"unpackedSize":912671,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZsy/CRA9TVsSAnZWagAAh/sP/0s8CTVUMxcugSfzUIdf\n6VykFAb9tauCp4T1223hGockKClA1fnFwwYGAdn+b95VymWYqrGQ+KiGRtZZ\nGtQMR+E10CFL4aIMuI5NqbQdIcRfu9VevDxBlhn4rRbcaQ5RitXvU8s47LVY\nbG18JBagHbnWFDOMRZQLy5XESvp+a1yMp4BsEtbNKv5Iwta+4fxrlT2vIxp5\nGJfRQHbDbSwPzFPuEmSoUUg/ZyQe6YbMBTeqDtqXm+yw+h8lNCXQqf1ZRix5\nkZTcPw0Tsu5nJ7yePqE9lAWNf8YJMIX6UMVEYHYt/wQInKYYsRKp7MYIW/a3\nou/SNO/0Z0lGmuYJXQptM8/2ufI6GD7QNZDw5+FwUAWJ+nj2en3mRFbxQveD\nI9DYsZYSy/3dgiNMVIyggz8CKwf33dZPXVbC+//Zl0ZBdKYjsk55jGsag0gV\nSsAkk/nKWwUQJ+GiIqTcmA4v9nQQjj6uBP5XmCrXSb7XlC979kr7I5nQ6cpM\neJqrofFCnO5T6oSEIst+JEY2s1EB59C27VxpeJRlSFJSUCjqk2oxuxypIGvT\nHS8xzp3714ceQksk6c4rJ9rbs4PxHBlHb0Sw4J264ul6TxAqbzXicpKXN1hl\nQ5i5JQJHgsSi7MDTCckFLx5FkprLrPsgZh6v1Wfkx0pRNE5GY0hgWKHponut\nOKzk\r\n=OGrz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCCQ349amOOzPcj4qjj1Q4SyipGQTGoYFHPQgqye6XwcwIhALyQGi4oVfyH8bEMOY1pG9wCelogAX8qfhY7JAk8BGmG"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.3_1617349823177_0.5877767486415779"},"_hasShrinkwrap":false},"2.12.0-canary.100.149d6cc8c6e8d0723fb89c639a3dffe1cb967380.0":{"name":"@sberdevices/assistant-client","version":"2.12.0-canary.100.149d6cc8c6e8d0723fb89c639a3dffe1cb967380.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"149d6cc8c6e8d0723fb89c639a3dffe1cb967380","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.12.0-canary.100.149d6cc8c6e8d0723fb89c639a3dffe1cb967380.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-RYmPQ54HFz91+XpBV2r2FyVzhF7DWRTN6G5WE9pFe5ls/2+9koIr1atJmLIZZJVfkGFdUGkqnoDlmkz6ciGqPQ==","shasum":"8330445001545c5dca1e5f431e6203bea193129d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.12.0-canary.100.149d6cc8c6e8d0723fb89c639a3dffe1cb967380.0.tgz","fileCount":50,"unpackedSize":913477,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZv2HCRA9TVsSAnZWagAAI8gP/jgmC+294VHivYioKCAn\nYYuPDw6sPjKEpjsb1YEnrnf/vqp2blAVOfIJkb4PzP7/dHBgm6H8k0dba9XE\nClIzBSi7YOVvWC3o5J0tHCbjPQ6toKiHIAMLFTmdndIspNCOWjqLP/7GfO6e\n0FIkDtryTrnMDCg+WZFtM6O/abr8TsQE446YULE2pTww6pAmFrQInDrkZa1P\nSTam+/u0r8GWwJBSnkb9yuOIHb+Lu5KdBcomK8I6LZEs24rJb3e01lMEtnli\nEnF5hJft1AHdP8TILgM/Ckl7ta/G39XJpPmUtNcU9Vtkzmd0R4f8OTT/8qmV\n0mZjQyiPm4gs6YXczsUWnFT5U6OlVnkKjbOcf9daWtCdEcoIjXcDQPvRgk9Z\nQwN9WpqagOwEd+Fe97rMgOSbkVeDlrEK9Q2wZCtZ9Y3TPq1y57qTYEBRq/Po\nSePWlVFeoMADq86aibxWL5F8VajPL/DO6Ddlre1bGvTRUlVy+Wu2kI/DmE5A\n2wup7Jn+IFRDSGMcoPEaxCDgHxBIqJcDIiiRv3oFNyakyY6BNMx+QRYWTJ6k\n9PnAcW84Y3qa3AJGkasNmF4iZlUNhMlKq9P+HOaVSuca4sRg9G6Vy0L8unKM\nE3X4vVhUcprv+HIiKbNp/Z1vDgGCzc5T+RZ75yyy/vznHfL8fGjQr7fNt6UW\nfC52\r\n=8efw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCzjN2LBVGjUn5yDXqW2siKdE0Hn2PyvHTjS095uUQR3gIgZhueqa256ob9n4GSZknoHXLbxfl5QxjixeKaD9AwiUE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.12.0-canary.100.149d6cc8c6e8d0723fb89c639a3dffe1cb967380.0_1617362311054_0.7060072047843973"},"_hasShrinkwrap":false},"2.11.4-canary.100.aa24d4618e2180205df142e5c61b3bcd7d97f92e.0":{"name":"@sberdevices/assistant-client","version":"2.11.4-canary.100.aa24d4618e2180205df142e5c61b3bcd7d97f92e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"aa24d4618e2180205df142e5c61b3bcd7d97f92e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.4-canary.100.aa24d4618e2180205df142e5c61b3bcd7d97f92e.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-q+sv/ESzB+bs6GGHWftXepfaCjrNaRJGR25y8skM+fgpULsSCKK3DEg8Vq+3/c6Blc6xOs9+Bz5R+uwzowPGqQ==","shasum":"edf3ba9b5e1f0bc8a311cbabf602af91e6640ea7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.4-canary.100.aa24d4618e2180205df142e5c61b3bcd7d97f92e.0.tgz","fileCount":50,"unpackedSize":913477,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZv6eCRA9TVsSAnZWagAAqYUP/AyP5bAIfMA7ggSI61/4\nBbKGYIO5zcskLOZz38ygVpjx7rKD1oG3KG+8khnrN14q2dz4XMEaTXP0Jska\n3354Pt+cTAiz6bdtib6w3wcsav0tn04UZTgLtiediRWO6SuxP47LotmHV3fj\nXzuVpMb+0B6n9YQLDVY66xt3brxAiUYr6hweYmAbo2ZCrVdNHT3jr4qB+aRk\nZ/XKPtFMEddF0BFlXJ7NC2p88gP7GpqQ60nwWltZmLzhVcbjuZ0gAgba5pv+\nluJGSL1DxXolutIzjPTh2NKyzqnAtUwEXJxAMFqZJMGTV3WJguDxH/mvqnSd\n9xdpyzGIMKU7SNs+4qtzzSgxMKZ/LCFiXopok4/xdpJkcx/GiLHgTRCKuRcS\n8rt3pRHyv9Gd9vWgxro1KxwLyPdP7USQajz7pwu20ldAZNLt4diXwv2v5uZi\ntax0eTS9KT2jj5wjnNEz3+kfm0OrJxFh8lqCx3L3BNA5b5qxwTK2q8Y9gscr\nGq9Eox1NacLNB8xV9R2flVEMv5ulHju4AEfoRssYjiGl0ffqk3PStWMnigwh\nm5Qk6/4n6cVuyhgTQn0Tk/zRMPMpoyDFbN4xOZRQ/cuh3qM370RWPauQPxQh\nL8izsdOrGtF0aakX4oQBF3rqaPxhYMLTKaWEYLtd6uiY5N7vtPYa37J/BiYz\nM/wF\r\n=KzAi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIByWpmj9OqZpqDkUe6T1ryGjt2ecjckVHh5YnZqbVw+SAiBwbpqna2v7NyqeaQRMai3g4sCo4U8VIqzOsbfKEu0O6w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.4-canary.100.aa24d4618e2180205df142e5c61b3bcd7d97f92e.0_1617362589861_0.6416010804418553"},"_hasShrinkwrap":false},"2.11.4-canary.100.2a5b040a3fafda6d1041a2287c3bf819d04c3387.0":{"name":"@sberdevices/assistant-client","version":"2.11.4-canary.100.2a5b040a3fafda6d1041a2287c3bf819d04c3387.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2a5b040a3fafda6d1041a2287c3bf819d04c3387","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.4-canary.100.2a5b040a3fafda6d1041a2287c3bf819d04c3387.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-xPDUNKkq+KL0hlLh4ngHhrJrEr4dBipDSyFaZgBmUgVMsTU1+Q05ABWRtGEn0C7AAz5InvIWgBxdEP5dAt4bqQ==","shasum":"91d54279ef8b865ac1a1ae09fbe2edf4bd09837d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.4-canary.100.2a5b040a3fafda6d1041a2287c3bf819d04c3387.0.tgz","fileCount":50,"unpackedSize":913533,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZv95CRA9TVsSAnZWagAAKgQQAKGce1llE1CGkhT1++kP\nO6aG+vw0GsnaHHpaog+GczEb16W6ugOEGWhcv4DEbUEZx9KOPQ8z7+/4L3w4\n1VZyCxltsJ+NORyZu0FZ3UHzO29zlwV0Haf/pjlR+f8eGXuIxvaHqesGzpvr\n2LbHvRX1ObpouMS6YyYpd/dTIg5DDj0W8Y/ccajSpgc4RFLkMS3qMpjQ3Fdo\nlYkNJMTS+H+4SDylOApJuM9hE87VrcrLX75I2hMi68uspJUnw3chMuL/EkGs\nRyRNt0NN8r71IM6vHu61CEtF5L2Xx6glMGOlbXS22dE3YQQAUelp8AKC1r2t\nFKIr18HtqZM1IZA7Ih0En0B+rlm79oi8CgFdikmbRUQ7hsuZjDTeNGiv5yvP\nSxKtLv1URw5Jql6eOtOTURaQMs+iFdp3Ntp/mgJfGlVwboaz9S2cZh0/V6xq\nOnvkhLLqJXZTJcJeAHWEdQXwH/+x81DxHH1HBEtoETlXdIbTZcAM1K1m+7N4\nEw6nOU9TMj2stBdKQ4iZQ28XyAH2l0EGxsDhvobVkpCd+h75VopsYzrKBgHm\nBzJp94JwCst1iXUMYe+RFIRBKeRurWeWETgi9+iNqKG08DFdABxvFeeXuta6\nqr5jxEYlfLW9bx0dp3G3px539EYet1cXmII1iS4O8vGEUFb3rivyB4TdDD9E\n+7oH\r\n=Eg7u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDvFzxjM1OZi2nWxz7D7gtFj7C2eQpnqFsvkhquzY6FqAiEA7IUPVBi07hQJdCDEk8nWMTPe/4Hi+dccDij/QRWiCKA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.4-canary.100.2a5b040a3fafda6d1041a2287c3bf819d04c3387.0_1617362809295_0.21777402172598803"},"_hasShrinkwrap":false},"2.11.4":{"name":"@sberdevices/assistant-client","version":"2.11.4","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f4a2d47bda56c7a5c6dfe36061927a11a0523a4f","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.4","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-UfOl+ZIuoRkAbtvwxB09xRqYsTXSkTa0X0fcAfDq1E4bcTCqQT2Jn78bl3GUutghQ0m+t/4H1o6Z7rNjtYQ9IA==","shasum":"ce7bc0dbf5bffc8c0a579360a90166c216bd3cf6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.4.tgz","fileCount":50,"unpackedSize":913661,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZv+pCRA9TVsSAnZWagAAxogP/0IRTAY2U7Q0pplXXe+k\nENpGiPwtuUbw2GxbeV0Z8rt/CnOQ0JZk72pdD32PUZelGqVBSucWS5a8Grp6\nVeov6yZ99PztOvN8IuVq6DF95E+PHs3ZKm8Te7hblUM71+8IDYdgYzMmbMVr\nL8LbfdvjSOmDBv1l8Qvmm+j8LSd1s27IEK5S95EfohMfvFG+vw5IaO16yR3C\nMIX9M/jzjTIeHHjH/RDaBkfTotLUlS64LvU9+3XkDGidUkBVrUfyqz6AZBN/\n64Vc5RnZCpxMshlMURDlZ++3cbMUfs4+kYTLKf6wVBPrNklnjQWRu3N2iD+H\nST8CTpu6hjFFNmhWMhW5Y8P2SGyDmqt7xTlaUWLnfMX7x7FPJbcdYamSwBtc\nWZPN150R9yrtIDVaQcqv/kni8GsZHHn1hG/yIchiLDWsqokPUN2EyBHEXp19\nUUWkBTYIM1CErXzkLO6TQ9nYlXsuYEYJnlfrCcDmxzrspazZS+4FgTMtrDjA\nflMDb5y35atxCwTRsGQX7C/IE4ajtGBAVeMHwTbCx2P2vMYFxw/UX1MQXmrS\ncD/BcP+kBg424CoykitiTXAt29+4BG9zFMlhNzMXQxR/IZuqRDP/u0EDX9ux\nbuivKakId6YRjd1NWhOj+fze+oAfWjIdbSHorHWaWER+r54xlPHFj11wyR4h\nIE2/\r\n=T/Wf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDV+8U9+3L8JOfxoOih6EQTEL26Uu0q0ZOou+zKcJsMQQIhAPEcLSpppv2qcD1XXvN3KKi0Uj4maoEaDwLbT83S3Tez"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.4_1617362856485_0.6625501557135147"},"_hasShrinkwrap":false},"2.11.5-canary.102.1bf6d5fe2795c516fabcfb2fcb2a6717f917dea4.0":{"name":"@sberdevices/assistant-client","version":"2.11.5-canary.102.1bf6d5fe2795c516fabcfb2fcb2a6717f917dea4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1bf6d5fe2795c516fabcfb2fcb2a6717f917dea4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\nenum AssistantCharacter {\n    SBER = 'sber',\n    EVA = 'eva',\n    JOY = 'joy',\n}\n\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: AssistantCharacter\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.5-canary.102.1bf6d5fe2795c516fabcfb2fcb2a6717f917dea4.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-mHAnbCoFXGjLSDx/xN3n+BrS2oI6d6OBsvmzs4zC2YPKkEKkGeR/6EHI1i1ktJ5nfQcOa8ZQoHanFzx++1or5w==","shasum":"053d7280e7d354cec97fe90cb6ef0954b45bd02a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.5-canary.102.1bf6d5fe2795c516fabcfb2fcb2a6717f917dea4.0.tgz","fileCount":50,"unpackedSize":914369,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZw49CRA9TVsSAnZWagAA9XcP/Ai7yb7AVrNaShI325uc\nzgy6wN/ZQmrojtSCDpe8GK7OVEHkZw+rVqZ+7YtJ94+gA5c4ye0Uvmaj6GhK\nUt7BXN2m/myqF9+9rZVgzs0OUTwrq0yeLhlIX1SEshORv1GsUTDZsvWfPdiL\nP2o8qoryJM7HKQdmlADKK29hi1T5UKezjDsMWJY3LAViy1FGSXNElfEZ2Ntn\ngxixwcDUVcaY4Bb0ET9etnVjupoGeu7LH/Y3ywzDKE3O/eT9bMmmCa0jfYTm\nln8Hk4Eh1fAb37iP8O/NQ+TI14oJzPa2U6cG6di+faV3jtEjlzw0E0hOCoXN\nUBrhrMJW7c8HmEq8ef5gOoVDbDn5tr5va5JRC0UTQ6xDSGiU9g5fQFfiYwDV\ngMfIKVmcYJTn/fpsWUiaeNMz/uHa5Il0lomQBpc5xc4Wx0CrKimW4/kKjzvr\n3PXe08kZC6nmsB+PnVKOfupZp3WKWmGY1L/Jva8WQhasP5vqwZ/QpbTD4hnR\nykgWc3cR0D+Ctg9Uc7lXkDyTq7/t+t2bMGXc8gT+MM8rLCmFriQPXq8/rqzu\n3yuQAApWmNvep+3DZsh2Hg2hWPYOFQPTk/oj7OPVE9owro4Sr3x4OrkP+Pwc\n3k0tQ6jNTwK2GbPzX3zyWw6u6e8WGBOq8jrb1pzMssQLuj7f/O6PunoTGNKD\n4s8a\r\n=z7of\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUQJeSd4YpSf0MXne7AMuWqI5g4GUzcYM9cPnMg6XT7QIhAM4xvBzlt7lKgBVcsYzAMFHzZ6w/WR5ck54/onv1X/Ou"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.5-canary.102.1bf6d5fe2795c516fabcfb2fcb2a6717f917dea4.0_1617366588496_0.031798260514214416"},"_hasShrinkwrap":false},"2.11.5-canary.104.8ee09d5d6fe7a200745740bb1dba5fe7449f0edb.0":{"name":"@sberdevices/assistant-client","version":"2.11.5-canary.104.8ee09d5d6fe7a200745740bb1dba5fe7449f0edb.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8ee09d5d6fe7a200745740bb1dba5fe7449f0edb","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdkMeta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.5-canary.104.8ee09d5d6fe7a200745740bb1dba5fe7449f0edb.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-V+kpd6LQRQsj6LxsySmDf3XKsnvh14jFd88qTYC7a1M8RNhHuXZ32vXClw9lejmSUToD2nGXD9JLh7D9MjkJ2Q==","shasum":"8f40183716678fafd738de1200c23c7dee73f452","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.5-canary.104.8ee09d5d6fe7a200745740bb1dba5fe7449f0edb.0.tgz","fileCount":50,"unpackedSize":913840,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgaukzCRA9TVsSAnZWagAA9yUQAJeF8VTdy+UjEvG5rBxd\neZVGQ2aKlVT9PFyPRjBf6oETw9xY3OiUIHMmTCp8+2XnA2EhB+OJpImLqHVG\nwOY29qUXwibdD9fk198xM+UTq2YS6fdRbIV1Zu5iNeGdTJ2qhjEaMPiI/W3R\nvNGgmW4Ir/c+KzFHYwlNmEYlkgQkAj1JRDSLtX5LCXCBYwSC6jefTDLvRLwh\n8wOckn15rJQLx+bpJSxJ50i3HosnFap3aaiq5Esp5p5pg0I+80C9rUf0VjHf\nepHTIzX72J7DUGr28gO1wJVd0wT9tSsKGrmQ7AtiNT8vsU3wjBylPMIQZpR/\nuu2DeQsjoDBOBCaSgl0wykGdsLZem4i+EP16swvevxYbJxlrW9zuMV24X4Tv\niaPi8k3jSCOEvrqMaZ97xqQdTY6oYhQS/cy3+wM/lniRsBIe+V0nbbiVQIn3\niijRW0QVeKUUar77OFhkhTSHkiVFN0Ck5muhE5dsvNoktJ39K50XdHZnqVGT\nd3xA2OFj9HLfLjeoq8TRCdvNt96QhjVgOSa3JUaKzMYDCBcN0Ci+QeoQjlzC\nvKWfKUkOmAQ/BFjcCe3br4kZv5Ole+qQBpVVG2pTZ1FVdMmEluhxQJeremIg\nTdrEvr0Bpxys2iwmsyADl2EGO3hnsAvPRcBWjIgnjzVgTz5ttdVBmt6X2Rsn\no4PS\r\n=/Oi7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH3UPJYJcdPJS6P529kaKYFb+JIrOKLCol0KQVbAMKKDAiEAipJz8FhUKlaflukaIx8PqhxBNSgwSJkMcL5dg9j0vdk="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.5-canary.104.8ee09d5d6fe7a200745740bb1dba5fe7449f0edb.0_1617619250579_0.335144235100026"},"_hasShrinkwrap":false},"2.11.5-canary.104.ffe2f649cf45d6178a95459d479dd53d47676c8a.0":{"name":"@sberdevices/assistant-client","version":"2.11.5-canary.104.ffe2f649cf45d6178a95459d479dd53d47676c8a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ffe2f649cf45d6178a95459d479dd53d47676c8a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" | \"BACK\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.5-canary.104.ffe2f649cf45d6178a95459d479dd53d47676c8a.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-Apd5p1/QVgJrt3rAnIwDxeein6jftxfJQ7HAFTQT81Uh8fEBN6nh48X6wnK0BIbL2UtvXiBNUYNJAk0I0jrAsg==","shasum":"68a317025e834ecdddd2ec96df4e3760e4819bd5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.5-canary.104.ffe2f649cf45d6178a95459d479dd53d47676c8a.0.tgz","fileCount":50,"unpackedSize":913918,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgavCLCRA9TVsSAnZWagAAS6sQAKHKjFyxGnN6Z13OVal7\n70ER8lyN/N0th5/Bn5xYDleBtN1E4jZeBFTluCLt/iQgpO/8Un4EPF03W89o\ntl8HlATjyuaf7q1VtoECg0PU6Wa8S4kwDyt3jNWLDYrmSkms7OPzZMMBss4y\ntpCXfdUI4XPtMMt8B8jXMfgCsjEpkwj79YvcV6U8zJbRYDUqw8/7mzK+UcLp\nM4xw78MxWEjm/Fog6IkiDxigzVGf/Axm91LpT+jJ2JL1snsFri4jG1aru1hC\nULqpg0aQGsgH5iY+npPtPhYwRSsPycxW5NJKaVjj05IMd2h6nWRa1vjKmYQu\nt3XJfYR/3YC8InddMkBEcMsW3ESpzqerJHd0EnCYc9aOIPK30DR7KauT/WeB\nHWFCPsc30W8izReVnAiU2iMuk45oZgUqkAAjHoLmWTMigxBPmbnJHNhaknWT\nJGH75k8cq5rDrr2UMfdvWGyWCojLv1i87V4QjsXDXa5FtfTCIQbEoDqNZRIr\nlLiA3UACdYdFNCYW7iuD87Q/+yvJEGA6+5aTfbHy7VariwQI6TrS7TpNcWBC\nuyUQ5+7Q+oWssOKkTrZ5pzYZa0rCU2DEogQsmLSx8KGNL1TiQHi+ABhtrXEn\ngUVV7RtNM7Ibs5jPdR73RjLBW/AjoDgnfgURJzu0/ACYAjtJNJTUe1aww0Vm\nOMIl\r\n=WlGg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDrf9P1BaLLzOd9JAEL1WEG9A76fMQ2rXGWljk+Cd6BEwIhAN6/D8XmHltBUmXvz/KUlFEWg0WHN2YE6wYh4BNRuZU2"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.5-canary.104.ffe2f649cf45d6178a95459d479dd53d47676c8a.0_1617621130821_0.74333416313626"},"_hasShrinkwrap":false},"2.11.5":{"name":"@sberdevices/assistant-client","version":"2.11.5","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9109b3b9589d1926dd808be1993a051177166020","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.5","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-ZRWPR44VnpfiTkaxP7lc0S8VRNMpCitrvC2qWMM54OCfJxtvKs034rQ8J7/rC3tji5s1jFN3gScECl74LQK33w==","shasum":"efbff6ce48d9eb6fa620de72d49819b8f92cb94f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.5.tgz","fileCount":50,"unpackedSize":914073,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgavGpCRA9TVsSAnZWagAAKlIP/3bVzjhv0YoyN/OTtbmz\nwffK6e2kyF4bnQyhA10udPm+E8s+CJlRuIcovlN4AfpcifSiwixdJriq3b4e\nybNFPQFZqAf7hMbdr1RLe+K0FE4OYsEvgja2g3rp1RE2NsQt+gtx4DmKTqNV\nBAmfRrSkjm70GrOTVX/b8kWtATGMfOTKF351fXtgajQhssXtzXtgmS4N3cJV\nmm14rCaxtjDdzq15T7xTJky1TxQuePE/G/x5iLsUQ5yKRljvQ9TwEt9hKISh\n7Y+80fixa6TELvdLJRhtKsq9x9FvxJOqv94ZzXA82SGezhrqZOcaJHMaeZy7\nqU22v/Ola9y1jOSDw5ibwulbE+1DDl9kxFajZ3DQ9jutgUF16yT23mMSGgIT\nYcnHSYbYdx/DaGKr4lVou+0132BQHY4tZ6nFVOyFkZ6UGrFPDyJST+JeAALK\nN/riiXbgvYyRVd4xGJ/JxEAHPsalgPiYuaxQGkk6Tw7slEcU8ObRXwuKuqXI\nFJgMT/45NFDqnQ7r6VWShx9P08h3SBMDN3ZbTwjVWCYfXKxpH/NaTNrJ3C7m\n1gB1bQ/HnH3cR9100nHVpevrcbI1PIBtPy0bQHY4twtJNxT8CC8LVLvzW0f4\nKBiXv2IxNcUoWjBsJ0ILrzTyfyKBb6GIWawzi55kXamTO4mv5GA3p9CePA4n\npq0v\r\n=MoE+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF1LsWREikEqMiU4fJC3lHJ/pCrtUubxvqV3Xse/d71xAiEAg7VyrTjUOwJUdSyXw17NIg1Qa3GsYbmWaVk43Lx9wO4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.5_1617621417032_0.15609055862483778"},"_hasShrinkwrap":false},"2.11.6-canary.105.48986d606ba17c128a7d05a26967b15117c875b7.0":{"name":"@sberdevices/assistant-client","version":"2.11.6-canary.105.48986d606ba17c128a7d05a26967b15117c875b7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"48986d606ba17c128a7d05a26967b15117c875b7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.6-canary.105.48986d606ba17c128a7d05a26967b15117c875b7.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-3kYTFnLFKUG7fNKKB8KKjoiBGU0XsAyMJpqVToB49/orJEEwoUJ5sdzE/CeZO4j/NcCHx9+UQOCMQOn+Y3+e0g==","shasum":"6f939c0c4d2b8ed36c6967581741ea48c4dc7ff4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.6-canary.105.48986d606ba17c128a7d05a26967b15117c875b7.0.tgz","fileCount":50,"unpackedSize":915021,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgbZWCCRA9TVsSAnZWagAA8kUP/1vzh6ot9pgscHxVsRZn\nvb0JJ6eq4zXnOKqCM1a0Ksh7cI8gl6rh9JvWZMgXJl38wsmJBmZ90BDpo1Wc\nD7AQbE6Vb87dZw/sKxGkgEL/zwRn5wlrrXiTcLc0sj37XXjF98hDpGhtMZKU\nP39lCEQUpGrbjoPhXp0coGwihP7R7ABf9RZ8tRnuY1SQSdZMvAOatRiYYxOP\n9etLB5Tyh70m0JPldusQ6ZT9t8agf6EIPsWda0ZVVahnRodHSivGFsmVm7HP\nOL4ZSFAK9gucCSCKWPcBg+bldR1k1kXc4Bf589Q40OYTxeoLeL6+imFVPGvo\nM34iQkL/C6/AZV/XUxNGLkm1elCAAmx3M1BEAS4A6WrSn3uFCzf3xzb94DmL\nJK8qmN79mIhnhEtJAXLfHM7XB4YsxtIlTYnmmIQ1VVUyzedyTQzH4Iq6mIun\nagSNlYGjTdy6VczsyJfMfN0alkTvppmYluwFN5xGEN2ITNIkJf5V7ydxHcjv\n2ymh3iY+FAQTNnmTkgrirdVoyRnfvHHHOR6y1BfxV65Wxh7Syn7w7GjiOvkj\nV4qucg/Pm4NIqoj8D1i4ubjUYo1+/IMCCc1ynZJ3y/1A88ZvF0NNSid6GDR6\nYCWDmzMHNERid/26jQJ3bLF7vLGScyoswJeGs7vOd6by72ExXefE90tjGglw\ntmDg\r\n=366V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAtxUYJO3+h/+jQgCSQuA0YWpqcD55YfDEGBwtCLkVI5AiEApT9xqUoCeFv5WsKmj+WwDIUOlqKTrMf2uDrlwzSzKcQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.6-canary.105.48986d606ba17c128a7d05a26967b15117c875b7.0_1617794433522_0.01798523552374509"},"_hasShrinkwrap":false},"2.11.6-canary.105.716a05dce76c52c9d55c6b817433a3fc6cca5fcf.0":{"name":"@sberdevices/assistant-client","version":"2.11.6-canary.105.716a05dce76c52c9d55c6b817433a3fc6cca5fcf.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"716a05dce76c52c9d55c6b817433a3fc6cca5fcf","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): any\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => any)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  [key: string]: any;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: any;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.6-canary.105.716a05dce76c52c9d55c6b817433a3fc6cca5fcf.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-5Mi0P80UAg+0NKMHlk4RxejPkDjS3Odq11C///Ddh63fIDXE62Ehg6wpJfx1ABTNIVydDi/JbdrUBCvLH/EaQw==","shasum":"70fa30332d2ed9e41dd80d4c7f8830f720dd5b50","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.6-canary.105.716a05dce76c52c9d55c6b817433a3fc6cca5fcf.0.tgz","fileCount":50,"unpackedSize":915120,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgbarhCRA9TVsSAnZWagAAoDgP/3/6zRw/DZ2suo6C9xW/\nm0JYNLMDQV/mgUCM/PyTnhgGs0emuj+4OHml6oQUDDNXs3KfO1mDVqJgIaR2\n/tPNvgkv+6I52rItIyZ3nf95QhGKqN/453YGzlkbAUmYFY3GI1cI4n0VcRcT\nKKRgIaX2djPsiDE5gksdy4+jRG0ki2/oiUqg16L6m6/2yvrc1S5lQcSlorX5\nuu6uMjWbX+R/upR+UWUM3Io5fYcg2Xu4JjaWiHnrL2D+vyCEdIIRvuW1KH7s\nmCwgQMTuXJlhy0mKmwmn54ngtmbZBfHLzpkEjCaAS/Wpa7uwLAdGzQoaU8pf\nXdt33mgkm7AJApnpASGPbUF5hXB1dIsSodSbgcV+TKFFJT8h1JuxyxW5P3Zo\nVbpiVDvHWo+L3OKcCBFl5DXzmceg8huwwmbz4cS40QVhoPNn1zQXqD9V7BP3\n4kTi92542h8gvFePQ98i9U4Mi8tFdnBk8qlN8fXQTsLhbRIr2syKNL0QZGzc\n9pZAmN7VWXxFxyJ/JpKpd7h7POmFLRarLQVCX8eOzzKAKM9YZgQn4AeDfH45\ndJ+AhDO3peJglFBd/l4Wh+arqoTivRd7u2hgmMpm3cz8xogq8HAbUDL1P9Jo\nwNBti0emb93t5W5u3vNbtCaulHt0bwBoGJYIISVJIux0X0TbuWeIM5Z/dxIy\nq+r2\r\n=TTqn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAuUzesmQ9CRzYpQGAPuMDz8gaUTT3UbFy9VAycFguaqAiEAlIf4VhpxNmEmkaCGEbI1UhKMQQZat6SyBMcqVXO/+Lc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.6-canary.105.716a05dce76c52c9d55c6b817433a3fc6cca5fcf.0_1617799905252_0.8573153762660288"},"_hasShrinkwrap":false},"2.11.6":{"name":"@sberdevices/assistant-client","version":"2.11.6","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cbc15cba0f0937ad7b319652f7cfc5fc54fbc4ed","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.6","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-3hd3CW825s5cUb8j8iat1wnAmdyR5ge+Qj0IxEqsYWyznkFtMJDpDIhmi6DP4Hl0v9b81q4P1uHoqyUB+DFHPA==","shasum":"18d8be2d42aa47af53304a1028b23fbc993b2dec","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.6.tgz","fileCount":50,"unpackedSize":915255,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgbbP7CRA9TVsSAnZWagAA+fUQAIrHsQ0CbICsQ9wykmkr\n4A0P68huLHCfDge31wZZuAPmSe5RAs+8L71fACqL7r2KFbcqhS7QA+vWpho4\nYDQ5+4Avx5SyVLmriNwh4dRIk/PIx2HyPeOaW1yxLEPxjo6Pu3GcLkoETGva\nHgTQTKMWhgmbvMttuGhmlOkTQ1MxNr0LlzyPew4IukuFRD+XsarZbVuMulL8\nNnP7eF85fsfB3J9iA8xUvpDbnsVJSW1zSfGI8BrEnGtb8cY5WOj/0X8uf2Q8\nOsVEMuvvo1l8cNkPVR283dzpK+mlw4gxNyGfAClmun+xR1adVBC6j7zTLzIO\nrxSpu0SgviMMB6DAVE82ycUn/jG+/pOllnVP9xmWSTwzxqCaoCCtyx9zLSs1\n0USo3XMxwjFTxFd68rEwFiNJUSfyaoMSZGz5HhE7TkKdPL1ACw5lmYbsOchd\nxKEtzlokq/yRfc3aISIUmSMy1WvIK3dBVCYi2vNGac2MeHfMpqVNgNFEqSI1\nFA+FAJli4/m8pKT6yJ9RLljCsktGRq2JzqCWxuEA8XGOTk/k7Ejj9KsAa9Rb\nBv2avJba+K0ZXQ5qNoBxfUlXMB/F2VdnXocMu0UNb/cuvKOQrHJ4UNU/OpNG\n64002Ahp79IEM/+rm+g0li4VWnZQBe8GdkDy5RuUc/fUt04AwINfRgshUVch\norr/\r\n=AhHM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHXlCvQldoQQliFsSEbKq66LCX7wM/xN7/uvX1w4gR/cAiEAx1bvAa5XQLwnRaXsMExwbbFb9dXSFb68+LbFYxsCDjQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.6_1617802234755_0.9367492713259009"},"_hasShrinkwrap":false},"2.11.7-canary.108.50a6054b8d581c0fb7dbe8c21cb193b4e8c7fdf0.0":{"name":"@sberdevices/assistant-client","version":"2.11.7-canary.108.50a6054b8d581c0fb7dbe8c21cb193b4e8c7fdf0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"50a6054b8d581c0fb7dbe8c21cb193b4e8c7fdf0","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.7-canary.108.50a6054b8d581c0fb7dbe8c21cb193b4e8c7fdf0.0","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-+bfM82xkRRw9IH9GJdpc1wAE2rNfSCZD3y7t01weAdI1qmkvNnbz4sVWTeE+YGSqTkS4dnFgqSf7odyJv1vQ5A==","shasum":"3439ecad874b650b6bfb8ee44636b1ab909d2a4c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.7-canary.108.50a6054b8d581c0fb7dbe8c21cb193b4e8c7fdf0.0.tgz","fileCount":50,"unpackedSize":916672,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdEP7CRA9TVsSAnZWagAAJx8QAIA+92rE3QzoV2qQT7Nz\nKgJzXQ+DEhxdw+G1YpLt2KjAjqq+jsWGJuDer/EeQdJf19nwBOcb8LrM7Ype\nt9gGg1My9OZp8sxj+YTRs1UmLc25cuKLqJnzMbKzaGvCbcnujumMPBW3aaOj\nKPhDwphpYHdt8AS3wbN3G/IEmoSpMiWcTmfGOqMF9xrls+0KofA7Is7ssiUK\ncb1YeYTrCT/BuU3igkGdX3D5B0HRQhrTpa6PMiHpNO+LZWeNelhdVSGt/m+5\nF+a4p/JGw19tcd463QeoOxYYx+aXC/2sl+wtvoXw0KCpjYBCkiOv0lEn/DbB\nAtWg44fcOzjQIfCeMehlIOgkCcWzc0F1Iz1ZuVpqURjEF7cGoqygEolqDleS\noRhOjDUMELJiYAVDJA7R0xmoYG+38hs4iEMq12iYCztcK4VnuPdEmTq7VHLI\nPI0cwXT+wk1FDkhf3zFsdQxjB0qXkymMqsUXgUVfdlKVdfEYYultb2ulYXMv\nZ0KwtTeQ9FQqsbLT+KieYuyGdvtLnVJpzvlhZdxE4ExtQ4yjiKMmTGPYQ0to\n163ErdYIXwdarhZRpOA0/CaXs+uol1Uhagvn6cPA384NsD43zygjlc0FoOcF\nsHcB5dxHT81XZFzm2LsBV2wWG66YuXMxZPyNVgPLTqIO3xyNOxwFnzrp2vqP\nTCH3\r\n=Lsli\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICkPD9L45hPaqILfMBLZTrclBLyqxKm+dlZpRPautmWpAiB1dWPOoeRZERLSM3AGK+kOnN6TMIBxVDQpu24180RFAg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.7-canary.108.50a6054b8d581c0fb7dbe8c21cb193b4e8c7fdf0.0_1618232315024_0.5234300499176432"},"_hasShrinkwrap":false},"2.11.7-canary.108.ab8f7553e8399e0904fd92c817e6f5a74d7edf16.0":{"name":"@sberdevices/assistant-client","version":"2.11.7-canary.108.ab8f7553e8399e0904fd92c817e6f5a74d7edf16.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ab8f7553e8399e0904fd92c817e6f5a74d7edf16","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.7-canary.108.ab8f7553e8399e0904fd92c817e6f5a74d7edf16.0","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-8lki/itOU3HxYDT4g+llUyaretDnmKEvdZdlWbHb6TQzyrNO6iPjnUKHoYea3DRDdc1baUgfpdWoDnW2bcuauA==","shasum":"5840443010852bca39dbd77641dabf78496df899","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.7-canary.108.ab8f7553e8399e0904fd92c817e6f5a74d7edf16.0.tgz","fileCount":50,"unpackedSize":916682,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdViiCRA9TVsSAnZWagAAh+0P/RMiK0AwabDgoqdBXkAt\nE/+DojjIG1zNz5z8QNf0RrcHvC+euOUs8+BV88vXLfsaaCOa/QF/2Uqed2ph\nxZgH0kX/Euzk8mUiyNBrs5C4Hc+KrsorJOd+hb/uwhet3qGzqJ5u6UJfXroh\n9/pik0rbyq4iR3SpdzwxIW44O8/jCjHpaZxznx493oNhbsTdUwmTONmdPPER\nD9/qdfPKSVLIIIkOeQtQnAH5IQ92EkBZOXgpr4gr96oXRZN5kqVKbx1HRN0A\nfiOF/8tb4wXXhwyFN9x844pzy8FZ2sd5VrIEEljRj2wzBjKILdX56IR6UhFN\naeV3ZjdzJH880HozVUsYm1j7CJGECdUgW0APeiC3rRgy9jUME5seDLe97n1h\nYT3uOWXdL3kg4KGuaQsaCEygik3A2xuKZMqqPRkbdJzBYhLSntgHcK5emCgi\nnfH4DGtPQPZZTGSIUUoC8DxZJNw0Vs+kDsUSOZNu3BAOTt8woOrMddZ+MR/m\nFu7L3vskPiSOdiYMM7SpculnEhYQOOoYN3AbALe0WKMvGWJxS7b0nagwO34+\njn8d9ltEobPlwRo4R1nRlvW1sm63N3GIPzAHkfFWdY0Ex34eTO0TqISj2VUq\nHd+GqU8S7xrX6i0ylRWkww1MvEsLkr5pD4w6d7JKsawkNIkWE2Lp066y8q9T\n3rQv\r\n=5jxK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGV1ToiRw1kkRHCBeUnTCjICFBgtU34rAXhxPyy8iSwEAiEA8yOCmhiCRKfDmdF0KTgKoDHa7baw79zDlWJylrR1pmE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.7-canary.108.ab8f7553e8399e0904fd92c817e6f5a74d7edf16.0_1618303138089_0.6318091783160551"},"_hasShrinkwrap":false},"2.11.7-canary.108.ca7e45a83cbfec1c1e7cafd3897761592d270f88.0":{"name":"@sberdevices/assistant-client","version":"2.11.7-canary.108.ca7e45a83cbfec1c1e7cafd3897761592d270f88.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"10.2.0","@auto-it/npm":"10.2.0","@auto-it/slack":"^10.3.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"10.2.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ca7e45a83cbfec1c1e7cafd3897761592d270f88","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.7-canary.108.ca7e45a83cbfec1c1e7cafd3897761592d270f88.0","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-AvAuJiJ60GYRsXL+JQVFU8Bya4n5G3WIBKiFv9kdTTySGvpY46mQvpb6W03epgeWfv+kOn0/6qHeQRT/matneg==","shasum":"707a6b0715efe2187da52d46cd441f64f7d8563b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.7-canary.108.ca7e45a83cbfec1c1e7cafd3897761592d270f88.0.tgz","fileCount":50,"unpackedSize":916688,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdYceCRA9TVsSAnZWagAAowQP+gP1c2H9RaKIftajMeoB\nIgG+Rg6M6OBZ9lmV4yPTM6lHC2y165Za+oYibEhM1UrvAZWF9L8cEfcYmjSG\n3Ztd6yXdyAUmyJb7VPaSBbWwp6B2j8XXS1/9iOGu+6FCyezndD7+XzWmGW8p\nCQdW2nrKDt1s8yoP/1bgKkLeT4q0GkkzXkf3z68dR7Bt7jMnaHbcZDbIao1q\nqw1rqOidC2/+dbgxaPZEgtrCIAY8n7zNyssAHIrSs8aioZnnytF8Fj+4oz5p\n9qldqLl31WGyxpFCKwLmzV2uALwJBnEfHjFCZg8Il+kTn2hC9VUSowWYymRe\nWylwkl+pLp07j+mq67H+3qA7ZgJrV6SXFF1n04iB87mJb+ulimlySD2D956z\n6U+gYg545PlePOmz36VJVaZoCRpXOh522RUfdGAM1gpJCyiZV6yAsoI+dnFQ\nn/QXEzWfHiTZmrnbgL/nN3/+dSo69aNv4kMq/EpneRulB0k8Kc1JJE0XPkbK\nQrj7OgRiS5wI4yho6zh3D/Ry1Jwcxy5dUxBxCXYMHtA7fCk1V5vJYs9oH94M\nZX1Pu5dpjYKxQUERG88wYOrIDkILgXIpnwzpU5F4UD5ABqvnDdp9XGl/zCZ/\nHYk/apzXyzfSjBSLLj7D7iiaZe+N+HAY3tmCeZfrOwPxhXKMq5mBI7Cg5vC/\niwNt\r\n=fis1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHJiRQ2e79mdlJiv5Z/VIFonAsTnSnJgzOls5SpvDI5qAiAzhvNuKWXtTxTGszHYF9TiYu3NiJrGZ91DsO3aLDgjFg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.7-canary.108.ca7e45a83cbfec1c1e7cafd3897761592d270f88.0_1618315038000_0.11989908698666962"},"_hasShrinkwrap":false},"2.11.7--canary.109.8b91d7e65657bce0b2291d2dfa03a6d935f251ef.0":{"name":"@sberdevices/assistant-client","version":"2.11.7--canary.109.8b91d7e65657bce0b2291d2dfa03a6d935f251ef.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8b91d7e65657bce0b2291d2dfa03a6d935f251ef","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.7--canary.109.8b91d7e65657bce0b2291d2dfa03a6d935f251ef.0","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-ovseXtKFJPXJyNXSuUW6DQW2F6D2L2QqMhXhrxjPIfC7VD0qqUgHdBoOEfYKr14OZc63GmchsvjIx/arFLlzSw==","shasum":"553532e0326ac70083f7b9adf61b687ef84fe6bc","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.7--canary.109.8b91d7e65657bce0b2291d2dfa03a6d935f251ef.0.tgz","fileCount":50,"unpackedSize":916698,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdqckCRA9TVsSAnZWagAAcMAP/1eUzQH1OIQc2VpFcFG0\nWQLwegcKyknhMzj1/tiph4vxZimsA2DDwp1ZuD63dr7B/8LD5v1FfmHxyeWJ\noxME4+XyyiuaXlNGO52xomdjW5DTzPqnr4Ja4Asb9Z9uA8Q2Ru3FMs3Gb4v/\nQlmgTjIzk1X/l6PxddMe5LiAjvtRBaxJmXfqh7QyDneXsCNCv71hHdz3Z2U3\n4FfRbDVrPJ8s5utZsx29EdmKJw+9NH3zeogHDU6YD8E/VEYbuBQ5dXca3IqX\nfLFyY5Vr2Uw52sJqToOwHr9eXF9bGsAXQyjnlxQ0AcX8HnAq8eUWUB+O13PW\ndZP7DRos/5dcM4W+1frkDfPvaMaTlw8kEyTHjvU8+pUg1+Q8cBB56E57wwCR\nLLCXrQgF/xbrZGVzuj/15ZJ+co4ZX4k+b87fDfWXtPXSSfcHT6IY5WWvoBUd\neApmkRe+7c6sYvEcfAobVs7h2PvFMqmutVxvKgU1fxmERWWatQu27FkVaBlZ\nEHD9RZOcZtETyAGEmjbdAuS8fAiDDgNoMn7z9Hwawcd4N7fWtpF8YkletqYf\nhyMzYX5iJ9YekuxZMvvjfLPIbpU8YfuzNfimGuXcSuVZYnlkL+WBUYqr/V9W\n2MzUQm5Rp60NCs5z25D12G1Ym9BgK+99fzSl4qADY3ZfIcocpV/CQa+zbgIt\nqp/t\r\n=DX+S\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDVP6rpZPt047wDz6CnfiaPmNLT7ExNfyUnqJ/vsaOGcgIgc8w1JsoT5KYq3hTMR9l4a4oUzLDlp8dqwObXz36uHjU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.7--canary.109.8b91d7e65657bce0b2291d2dfa03a6d935f251ef.0_1618388771548_0.5963053486705081"},"_hasShrinkwrap":false},"2.11.7--canary.109.6d633c21205286d91e9e2e16a77466a130c25b79.0":{"name":"@sberdevices/assistant-client","version":"2.11.7--canary.109.6d633c21205286d91e9e2e16a77466a130c25b79.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6d633c21205286d91e9e2e16a77466a130c25b79","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.7--canary.109.6d633c21205286d91e9e2e16a77466a130c25b79.0","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-kO0HEP1leI1+NUpPb2ilFC62TT9JT45laxIA//kp7AGQjB/b13J2Bw2U7p1Y/ZoESu8azsPCsrrc4laxSjkAHA==","shasum":"926231eabe7ddf54767c57d1b98ec8dd72296bd1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.7--canary.109.6d633c21205286d91e9e2e16a77466a130c25b79.0.tgz","fileCount":50,"unpackedSize":916698,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdqeCCRA9TVsSAnZWagAADD0P/0Wt0Y6CHm2PGfkkohVs\nCWJf/GAeyy7qrbo6WVAH/yFkaZfNODHpZ9AbT1BkPl1hNI91ZSf2uzu0nAf7\nY8qCe/MXdkohxSOqj43ZBtjC3UO5XVIL5GK22ZteH3lkqDKcOrsXVusfyB69\nQu958uzaGOWcr6Jo3ezG6fTPlM3EceSp+Cxkjqg3M48V5sBRVA8iEPIplviu\nPXbUHgHbjix/YvIJjoZEzlCiDTISOlo+SHLHXg1pwliRG+uTpTFWWJVhFu+7\nggtgL6ZypSoIuWZmgAvOsF/5haGl91JQb1YrrlA0pexpiKs+W+zcCu2a4eco\nWkLWcRTuiBBsG4Rax271vfm2ZV9tCHrLgZydPBjNPcSJMFHX1M0CIIwAvh2c\nRn276FHNudAhmBO4GNE7RWwizvnRbvStcxkFQ8v/XbSo27dWAIwX5KMZSkTu\nQOh7Pjes9Sn6B9O+z0B7Sx0pH46lBzOkJ+b/W7AACf7qWjmDI8f4vI66Jt+G\ntkuuiE/qLL6gtcT0V0NejWHYbsFSr0F4xVCv+YdF+xBGrwla1K3ZzcpqZzqM\nD5t/XNDzFYUQEihNoAT2193yb1uaVBffq7JwBeggyYzWcpXRRS8CmyEd5i+n\no67qSiABQZPYEma9Nxcz/skbxvd6wdcQzVCFRps9EGqzmNCeulmGQRP48iXX\nOg7/\r\n=54ns\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCsSQgGc2W+FSwAm8a4ED+dZsUQ0VYm5O1sIcQRWac5FAIhAP/mov+VuFXqqt3jU6DRgclmfVW6Iasu7X7/20x7Ue37"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.7--canary.109.6d633c21205286d91e9e2e16a77466a130c25b79.0_1618388865787_0.1005563930273059"},"_hasShrinkwrap":false},"2.11.7":{"name":"@sberdevices/assistant-client","version":"2.11.7","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"dd32c955d0a2fc2c01968f9e9cb4b550eab8a6b7","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.7","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-0w8JRGyqDy8vYILDr2iF/cfuRfvvXaD1utzdQXwbdMCXwQ98otaytMzmfylT0Dls/wyww0fKFiilRs8FfkfRhQ==","shasum":"7d06a15f687f423f4e776bc3b52a7e2d2ab37ff9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.7.tgz","fileCount":50,"unpackedSize":917227,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdqetCRA9TVsSAnZWagAAkb8QAIRtgos1oWc3iELiPTOa\nOVw9jJJZs04c8d82ooGborcvTNlPsyDYwGO36RSBaVjvGbpb0VJdUL6oHpM8\nQG2SdeupEK4CV9mXnALjpNNbdmdRxIIng1GjR1ARkSTzrl4yRM5SM875OU6r\nQqXG4J5/BkGjh2ojMsMVqVQuwhwGPwkSDXYjcNHBvDRHvcPl8kex5Hb8fkdm\nQ8Pghlh+ZqZUj+NXpQk+llj9ApeBbQ0M7NzKy3oMzeztOgQOUjbfKu8Mf97D\nD/RIFb4jtoVgdG58HafIIyghfHhpazMO0lhjjq7wfAqcw1O7ceX2qNywooNd\n7z3ACCv7jXleLjEdzjIxt6z103ik8NsvdLJiPW13NlbQ+wSl1n5M5XTkgLsY\nPpC7YDjrTOBtLG8A3j/gjQ7BgSEv2VsaBSK7OWYpPuyepuOVa+EbJUsZfTiC\nUsNniLqhwPbqvnoNg6mRXXcihHhuOkdlZ4Tg5HrkJVBOcidBgtcLl9tU1hAy\nPBcs+oeK/6FcU25mJG4iMaSgeEphz+DRQvV4qy3jk9EyMd6AumRaw5A2R/D2\nAZ43ovM4S9R11pMFWrydG/PJPR7ByuhpCoj+RfaffZqNTadOTGEEE95ODa7z\nzcNSwDH1/q6adpgXF+X9E1Uhv1qVMla+FbkBDxEnNHLEyllk8FN7VCc0vw1E\n6Yd6\r\n=tot0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFQroW7POd3N4QJJVqR/Dhwq7d3H/0tzE/ZWXklSDvQVAiEA1tq12jhw8ow505JS8Gki8v6U574BX/6iJDzT8i2cOiQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.7_1618388909118_0.13164117653657725"},"_hasShrinkwrap":false},"2.12.0--canary.110.ebfeaef35c5e7164b5c7c35cf32864c2ec1b21b1.0":{"name":"@sberdevices/assistant-client","version":"2.12.0--canary.110.ebfeaef35c5e7164b5c7c35cf32864c2ec1b21b1.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ebfeaef35c5e7164b5c7c35cf32864c2ec1b21b1","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.12.0--canary.110.ebfeaef35c5e7164b5c7c35cf32864c2ec1b21b1.0","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-8G2yii5cIt/TxBpTe+CxfM3p2xxmVKwexlyfNdU7l1/ecjsdO4f8wk1ACC6h2LKWxTMsBdMNGf58UwDGldUGZQ==","shasum":"1f0f38223bcce4c4fa7d9c652690d30180b6a6b1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.12.0--canary.110.ebfeaef35c5e7164b5c7c35cf32864c2ec1b21b1.0.tgz","fileCount":50,"unpackedSize":918696,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdt66CRA9TVsSAnZWagAAXBcP/isrYRA6CPOCU7L6Cebn\nCOlQjWdsel3KC2Ck3HevOCub6BT7PwVNDrpN1OYbtrf20ZCwVzeOrOSyZL1U\noIVDOAC6npKK/i/wf9slbOKIBaCAwl0omw1EOWsDsZbW1yIu09/m7A1IS9TW\nxn3SQ7dUi6g90oGDd9J38oX6ya6obhNdIPcYMs1B0FB9Oiw7dR7WRcQRxo8E\nNxlsry0VNu+xp3BGeJjRbpV0xKC4S1jLDKibT7qP3TRweRya3E6Gi7Dod1fQ\nd9WLaONyiwN2S3qydW1BSk6JlKXfIXMxeJRHlo4TtkmHafY0T9tyentUlCcF\nl05Vo8o3dTi/1mMf9L4LmWNgGGH5hxxGrf1ydBEbJUyvAV9Z7hmPfmdrWZ0h\nyS73y5O3f++i7QSlPWFOD9w89MpbkL7P8YZwovwiRxg2RpmBraa91BESOdgv\nFhNuFB+9gsyRHZeYb5ztHkfsTY59fCoI6LezcPeMAKdrIXvnngXKqk6yMPxC\npEqvsaenas3q/QnFAQILzLOhAbGxDFaBK1y3Qp6KIWBgzRxNS2UqUCbmWumj\nTgzsw8IZN00ErhKOPuI9H70Qce2VKhaqxunOz7iffp49w+IRPqZLjRdj6B7P\nk+pxhVK9QJWkkPzz0P/hYm6ZFX9g49tPrfLen4XblpaPz4IH/btm8ZOlgBkb\nVZJw\r\n=gJf/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE5UJ8fUbEDwEPiZhJ4ZZ/VqAxD+bvRuYGL77c0J/cHPAiB94zyyCFPSmyWiow6oaklcX6ineuWiypQTxhRMkMK2tQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.12.0--canary.110.ebfeaef35c5e7164b5c7c35cf32864c2ec1b21b1.0_1618403002435_0.68074807460984"},"_hasShrinkwrap":false},"2.11.8--canary.110.79495163f888e0e21ef7e72e49333a2bd1f18ace.0":{"name":"@sberdevices/assistant-client","version":"2.11.8--canary.110.79495163f888e0e21ef7e72e49333a2bd1f18ace.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"79495163f888e0e21ef7e72e49333a2bd1f18ace","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.8--canary.110.79495163f888e0e21ef7e72e49333a2bd1f18ace.0","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-ruPX8UcIXBNC/ULbJjNweKdOU6fdyr/s981JIiLf0OeAR1M2fUUQJrs6bxl10CqQkAiOp0X56Zd43hNQiNXksw==","shasum":"f57d5cf4bdb462ea96feca5f86fd6bfafa108051","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.8--canary.110.79495163f888e0e21ef7e72e49333a2bd1f18ace.0.tgz","fileCount":50,"unpackedSize":918696,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgduBXCRA9TVsSAnZWagAACTcP/iTueoenxOOer/l8y03E\n2WNjz+YKbBepm7cGJ/wtrLHzUpmvtCO31WKf2mEj57lzUpL5OYRkPcrJIFQY\nEuJfeNfmAesF/bAhOS/N21CbYrS4yWNO6PUbs5MIiq0Y6FQNGCVCidIySxSJ\nsmJkemQ75DoSq+77MKEpKoetP0ZnsZq1iA8CTxyv7U2I+dWhBv4CGlWFJSCc\ndb9EezdNgOp7lBf0OlFZhdfOf91wcruESndYvRgyyF8XqY87DIa24t9RXEd6\neH+cC0jcXUdEb5i29PfE5TjJioqVQJ61IrmLg32ZB7bhsyAkLJId0aZks7ZZ\ni9PuyuMytKJL7apwTsCGUittZTUps+8rcsr1XZ4G5NiShlswf5UGSon3+ZT9\nCQr7v7HPn3XpKpg4cdkLCSfDjVayHRfULcY4yFFMZMgrGL5BiWtj/h0adKsY\n8bNlwp7Yg9R8Wmr1B7fz9LnoEg1q01HCDwVnnqG+zb35B5U9rDB/wNkv2ozV\nvkBjX62M1TOEj3WnDohcZVcN5mPzU2N/nGOW8NkeDr+aDt7lPzxlmn3aAzJP\nmKDnmpssVq9x09fZtWjSZ5sNI3JkiM9yEKaBTOS5Wl2YWkQNLNfPAi9R10gx\n079WgVzrG/oJ3zJPLIIp01oDxGqqupK3Wdx0O478TlBzxMh0pMMQbK8hIv+p\nF4Tu\r\n=pw09\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFaHruMm9BE4bMdKksVotyjRogK2wgQRYL4aJd5kFtWTAiB9/XsqT0+3JaT7dx1C5yOEha+ht2183q+COZwo7c41Uw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.8--canary.110.79495163f888e0e21ef7e72e49333a2bd1f18ace.0_1618403414788_0.4760915801564751"},"_hasShrinkwrap":false},"2.11.8":{"name":"@sberdevices/assistant-client","version":"2.11.8","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d68248ea449a32d4ca8ef02e3fb7716f48b1bab4","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.8","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-idcI22M2bQTv53zAPcWH8A31iTSRlOBUO8HLrMlymyQNQsBp9AlyhYOaWIgjzW1KHOdJUJob9GHB39qvB9wXKg==","shasum":"94d055d0ef34ced958cc2f55542b3e15d25a1bc0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.8.tgz","fileCount":50,"unpackedSize":918844,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgduEkCRA9TVsSAnZWagAAaykQAKRVLgwsMz3qkzexQ7JU\n7+Ah3k1g6QxszsRpm4p1R74D2INgCyEuW2wySv+4pyJiSe3EShJtPM9TCuFr\ncTeK0Iigh//0rb/2u1Z/b7ztxZJtG0hh/KCYG3KTXmI9FBtAIUItJKML2+ZQ\nL9u00xcOQAlRYWyeB56f5QMCwjfVShpETme62A8Q+1kehnpp9G+T3uDT8KGr\nNfJg5Saa/Qrakrnbzx/QAIi4g++WZWehDT0ercqWrDfepI2NkEMmEAzte5G+\nVJVcPPqC/4ooKcDH9fgkQROy7uTUdvoLY1iBvE0y6Z55lih9VGbyRSsNwmD2\nbSutOYzNtkyY+Pioietidzf5eZeQls5eqJpvPGDTMLyYtnDxsFkn2mNXGaP1\n4AKkJXJrrTVbrGDYCwtUrW172CiieOtgw3pYxz0HwIjn7/Mdrz2VJhGSCIO7\nrduqfTH6ogbTeRytpgjQcY7atak9CiXvd+hHNJVnbxB6M0dV/jaZa4MdnHCc\nXi9AvbiQas2cSOoHEe4YP3EaId3Mv/6SZVMwyDs+mmC9dhWv0Et/b9SHyYvm\nJmnLq8dkVZfQYpLKzHJxJkGv4CM245XRCFUUnDek+gEBCU3pfJ3wznZeBxZf\nTzCnnrzDN0xZe4RhkUzIz892Vhsk1PffOg2P69IjGc1JtlPYUSq9es5hgUsr\n4jid\r\n=p3vi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCR9CbwTGi4BnwfEFfSo+LIjgL8RyekIGQjPfc0Xda+OAIgGuWfPyb6EuVPMbQcvpjnE5zdjDQ0jgeuOtXhBjyLzkg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.8_1618403619876_0.9643978205009245"},"_hasShrinkwrap":false},"2.11.9--canary.111.d28ec352e1f0d8c2d48ae191126675a2c804d9fe.0":{"name":"@sberdevices/assistant-client","version":"2.11.9--canary.111.d28ec352e1f0d8c2d48ae191126675a2c804d9fe.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d28ec352e1f0d8c2d48ae191126675a2c804d9fe","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.9--canary.111.d28ec352e1f0d8c2d48ae191126675a2c804d9fe.0","_nodeVersion":"12.22.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-55TDXORtE+YDGnbquSEwojElp3I42QOEWzwJ9vIYvfxVbkUmKjayGbRRkzAtbJHwjPLKNtDLGuahWdF0BGc3/A==","shasum":"b0905ea4f609a5ed8a2325a488663d8886048584","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.9--canary.111.d28ec352e1f0d8c2d48ae191126675a2c804d9fe.0.tgz","fileCount":50,"unpackedSize":920316,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgeATPCRA9TVsSAnZWagAADGYP/0TFAKcsCPXijZE3Y06n\nGLFXOci/b4abIgMDT26VzNdPgJJud+G/t94XZx0EaHyEO4asIH+3+E60nT29\ndH7Zj8wl71IDEQQr2eIIsaVWpTs8Pl45TOPWzuLd+bcqVtZOhA3VjU6qj4aQ\noxmFoVktshbINe+u0k4TSgVXIon1tYgno2f7ZmPGen7BmE14jwiLiELKCW7a\nNJBjGziFPMN2qAXJJXMnNkx/kdKE9QB0rTuQVkDrPIFUc9zmJX9TU69/UsKo\nz6LDi97PXf40D0Ue83Cfpx3ddnMyP1EG2f9WV8WYLEn920VRV20MM+GIr0GJ\nN1tIhQJEF2AzqGRIlSZAuNQoA8/x11MWNSRniyqn9TCFp0DmryYWz0/5xD/H\nH5MNt04Cae+sklR6OPT1Tmg3pO3QyliEQv2c/bspvKQ1Uzi9Jcbiz3++TuwH\n/cbffkkxYVHaf/aD+g5a9iFCpemVPbtyB5o3LSEutw5dmZP59la2mlFiMtEw\nOk7C5D5LEQ/EfuAwA3k4k7fZ1+axnVxmTa35DnrW1j3Yt5tbQROiiR7ND/TM\ngw7WhzE2crV90Rr2voKG2oKXNi2bIqS4uMETJ7z2NtcWb058xRPtpsQs9tuw\npfNK5I7paYrOqKkPCXpEsoJoGjTooC6OEXPMOk53t/QkXvdMzQ2NQLVfeQ3m\na6GA\r\n=1c2c\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCMmLJ4ixexU/EjYmLSDM8KuEE4F2bTIHxCcut8MZ6JMwIhAImvdZO8rVTc5qbsjNQDh9HRPBlBiLf5LT3swZWqqeeQ"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.9--canary.111.d28ec352e1f0d8c2d48ae191126675a2c804d9fe.0_1618478286969_0.19586683169202468"},"_hasShrinkwrap":false},"2.11.9--canary.111.2975ed6a45ecfcd7ddfb0e27b7bb24f760bf3a39.0":{"name":"@sberdevices/assistant-client","version":"2.11.9--canary.111.2975ed6a45ecfcd7ddfb0e27b7bb24f760bf3a39.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2975ed6a45ecfcd7ddfb0e27b7bb24f760bf3a39","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.9--canary.111.2975ed6a45ecfcd7ddfb0e27b7bb24f760bf3a39.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-M2170ypAF0GbDZBXPv/KRqjeIb07KfxuL+h+qlH3OkwZg7fjc4X7datVV7nKN56y5umAVwsnEGsBYZOsO66ayg==","shasum":"81406861681f1de8f161ba3971af7c621129e4ae","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.9--canary.111.2975ed6a45ecfcd7ddfb0e27b7bb24f760bf3a39.0.tgz","fileCount":50,"unpackedSize":920318,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgeTM/CRA9TVsSAnZWagAApjMP/1zsvLwsYYiYCoV9UJUt\nrBSK3iMs3AWTXlDoQwLJM4/AZ6zZqhBzK95/Ddl4UqdedrVT61ODHuOze19Z\nGsGeVPrZVjutRH789pxpnAA+hi24grVfcNN6/gpL/rdywx+2xc1OV1/R1dgC\n+QU6bshUDCOyT6ylMTJ9ZPbjPIVeOpvb/F/caob6ec7Cx4zigpVE8lj1walt\nK9QR4fegegqKemnWdxjG5eH1Y7S+EQhwCQX9bsY1DvG36mvBbihEe/7dOmFS\nwsSZ5G40kuf1RAvwEV/7JEQNUmIK/bP0tBXqf9zAEo/GctIdLI8VmGEOzBhD\nNwpN9741r7J1CKBS4xZK1NGNmbvGSdO9ExruzFe/MxPsq1YfNLI3W6uKTJPU\noBNLEJ6INURbnkAGuxsU+d0iEjmrNMC6nidL0VgRnSooAxWavAafnBrP6Bov\nhA+r3hjYjXhrVDTHnLzAG00Gv8T5KqigAx471N2NIRuXA3D23Ak4YyiKpJLD\nMgPtznEPb/+EShou2Z4JOU0D6/4CrsWT0v0DSQI0pKSte2oz4xA0MtzgR9rj\nhFHXTVz0I6InLNIV/bNRVy36Ul4ALprBxZ0F288AppS4RkCs61UU7enTumDk\nX4IJmH8+9nrB9u+RGQ9F5wPnLGpO5bmw8RgyVnaAyEakoQdsZUuBffLzEFlt\nQq6t\r\n=JtBF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEAk2S92dtAiL6QyJsiBqPia1ME3GPRHHpqxkK8vQmq6AiA7es8tEoBy0Cc1XOimF15h2zzGruaaSeIfVYojtAm5WA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.9--canary.111.2975ed6a45ecfcd7ddfb0e27b7bb24f760bf3a39.0_1618555709010_0.813760571409738"},"_hasShrinkwrap":false},"2.11.9--canary.111.bf547e8401c7516035e045f3318d79e29cffb029.0":{"name":"@sberdevices/assistant-client","version":"2.11.9--canary.111.bf547e8401c7516035e045f3318d79e29cffb029.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"bf547e8401c7516035e045f3318d79e29cffb029","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client - это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример использования - [небольшое Todo приложение](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), демонстрирует пример взаимодействия с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Навигация по страницам приложения](#навигация-по-страницам-приложения)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n   * [Требования](#требования-к-устройствам)\n   * [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать приложение с типом [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен в Кабинете разработчика и передать его в запросе.\n\nДля получения токена необходимо авторизоваться в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/login) и в рамках Кабинета разработчика перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. Полученный токен необходимо передавать в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant-client доступен для подключения через `<script>`. В этом случае, подключение react обязательно. Версии react и assistant-client можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска приложения\n            initPhrase: 'Хочу попкорн',\n            // Функция, которая возвращает текущее состояние приложения\n            getState,\n            // Функция, возвращающая состояние приложения, с которым приложение будет восстановлено при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду, с возможностью подписки на ответ.\n    // В обработчик assistant.on('data'), сообщение передано не будет\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // здесь обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку \"Салют\".\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде (Dev).\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из Кабинета Разработчика                                             |\n| initPhrase       | Да           |  Фраза, которая запускает приложение                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии приложения. Устройство запоминает последнее состояние, которое возвращает функция getRecoveryState при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенд.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nОтправляет команду бэкенду, позволяет передать обработчики ответа бэкенда и ошибки, полученной от бэкенд. Возвращает функцию для отписки.\n`sendAction` - дженерик, позволяет типизировать сообщения data и error. Вызов `clear()` выполняет отписку от сообщений бэкенда.\nОбработчик assistant.on('data') не получит эти сообщения бэкенда.\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенд на бэкенд через ассистента.\nПервым параметром (обязательным) - принимает данные для отправки.\nВторым параметром (опциональным) - принимает обработчик ответа (на переданные первым параметром данные); в этом случае - в on('data') ответ не придет.\nВозвращает функцию - вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние приложения.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске приложения. Данные приходят при вызове getRecoveryState.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает getState, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени - ответственность смартапа. Assistant Client в данном случае - это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенд для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит \"Покажи 1\", бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя \"1\", пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` - это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью приложения при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` - информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` - команда навигации пользователя по смартапу (например, \"вперед, назад, дальше\" и т.д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` - это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` - это команда передачи смартапу любых данных с бэкенд.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\nДля получения и обработки нажатия клавиш пульта сбербокс необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам приложения\n\nДля корректной обработки кнопки `back` и навигации по страницам приложения - необходимо строить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события; когда хотим выполнить изменение страницы вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента реализована утилита `createAssistantHostMock`. Ниже приведен пример использования, полный пример можно посмотреть [здесь](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\nВызывать `createAssistantHostMock` только при использовании [`createAssistant`](#createAssistant). Например, при использовании cypress, функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписаться на экшены фронтенда с определенным type, переданным первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтписаться от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмулирует команду, полученную от бэкенда. Команда придет подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nВозвращает `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписаться на события готовности утилиты, cb будет вызван по готовности к работе.\n\n\n### Запись лога сообщений между ассистентом и фронтендом\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками start и stop, кнопка save сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений между ассистентом и фронтендом\n\nПример пошагового воспроизведения лога сообщений, входящие сообщения, от ассистента, будут последовательно переданы подписчикам AssistantClient.on('data').\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд), возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов sendData) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагрузить указанную запись в плеер.\n\n____\n\n\n## Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.9--canary.111.bf547e8401c7516035e045f3318d79e29cffb029.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-eaTkCM55Q/sLx4i7FtBbJ/Qq2yPaEYzEYh7TBJH4b8SNq5yHwjzuJ3GVBT/xZX7ICizr6iVBZF7dd15E0dO7Uw==","shasum":"256a07d6eccc43b27aae0d6fcc4ca51b4ab21146","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.9--canary.111.bf547e8401c7516035e045f3318d79e29cffb029.0.tgz","fileCount":50,"unpackedSize":920318,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgeWZVCRA9TVsSAnZWagAAgtIQAJwahZSoJ5d3KUSo9EvJ\nODo1c7Q78/BifX+ZPfxZuEXlcXK/3lkc5zzyRqkiyz4UzzyIpWtPKh3EA0yS\na4TLTxNBoEgITaOr3+i7pAIsHtSkBp/kK1D9W6D/tjJuuJ2L1VKw6J3dzn0B\n6QxO3YmIJKDXtqvtsalTqhA5dO+qRYkhhkUoyjp6OeJ5AqfG7IljhKo7RlW9\nlxkWqOOp7SUmmAdmTqNVCJNMI5g70to1jdRBmK+zzHp2lnnKDYqTd0fwYYgv\nckljMUa0aTIbvztZstXKm+w22WXhnAF4dAFcXg/EUDBBfwrMJCD0yMv9QL6O\nELgeey0bJm5ARInfGxxVXwwvpmO/0OJgr7beZu8qhAnQgvqsT++kGI/Z7pJI\nx/LBtyEpsqkXLVpZ1DLvRxa79LSo7H+gZnArwvmdABk2GjX2KkltclKLems2\nasrKPPJrJz13Q4pElURVGK9YNT83alyyHUh6EUqyffDmlPaSjiAPcDHCDeco\nRyH5bS2oKeoxjK7rZ+AgM15cS3KK4UuvJLIxvnuysdAmfZK0wUk35SvacfDl\nuWXlgq6nkCNx/7C4o7KVp6FNQUEY7FFqlliqqkmWgQr0KDcsK/wAGO8b08ph\nW/yivzTPGwTrSYz0MHpTUe2er/hnaXvsetBqHXWlT1hBIBUtFBtDDOQ96OlD\nQ1rP\r\n=FsgR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCEPTAxSSqCmxcKSi4rzfXWh6J6tSqxLZabFFHmwc87PwIhAL0PtNZK8AUL335A+Wsga/fydpLddjE2gAIzXCacap5s"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.9--canary.111.bf547e8401c7516035e045f3318d79e29cffb029.0_1618568788654_0.3346456226542045"},"_hasShrinkwrap":false},"2.11.9":{"name":"@sberdevices/assistant-client","version":"2.11.9","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2b286ea759a257eca220b3c7db95fa84f296935a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.9","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-uTgWOJCCn52y6d3hYBb27SBXzEZGo1DENkD/ThR67M6tXsMwx9PgP1N37FmlE18uYQHMmkdyyuehuCVkxtW8/Q==","shasum":"e63c13399dfd6a85f2386a66857944e31b3fd077","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.9.tgz","fileCount":50,"unpackedSize":920418,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgeWczCRA9TVsSAnZWagAAko4P/08JzwiXrfrZ/g66K8Hm\nQ3UqqcF5ldGlKmpCn1Mujnko9ljpOF9GkgNXLl2Yu/RFwpIf4oidyKezEYYp\n/m88T87aHFvJJQCg5z9+d2BODvPZhbYox/V/PhrfnhazAX7aNo5z9OAhPZ6R\n9nZiHL7K51EzfkEPbr2zg9Qd58i86+JmqynqyFwv5Rlra+wjNkBxywsA2R2I\n1/Lpb8l9h+iche7HKRU9OxIws4M0SzXkNttQZtgB68M9IrQjs+dvqkqT2Pa5\nqdZ6UaAtQgdL4RmWYyAOkU9ijhDQ8eCUYQDLhFNTiZ4ygp6AK0xp7KoblGpS\nZ1Rzff8VuMngWu9L53oYZCsF5fOhpmko4HODSR0zcI9y/hlFSvvboST6w+T5\neqK1mAuONfrdtBOicwih9wXrgdGoDKZqCYXrHMEI/Ww30rP/e8seAWad9B7N\njNhk9I40iJK1qRARBeoxZ/D1kCetLe67M6cRlFBWTE8fFw5P4RxLwCCOWqOG\n6v0yEnFRcbiElVZ19GvVt4qPCG8xgxz/4caWHOKR76TBQlYYCMO9+rOim9iY\nmdmJVICkp2zopfx442zMcufo8w/gLej/nsoNeTGbtJc+acxVY6l6HW5O6swD\nvmeGYQGeptBRJCYMy6Y2LY4+oGAkRk5JnyZ+OU/DH/zTdyyauIIeeZYYTVYG\numvT\r\n=kXYh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC2bLaZB0iEf5FmYAJG/d+ew5I+ZPqVmxr56Qan5DeEwwIhAKVeQIPk8i8GDeaSb0e27AKdmXl71OAmZ7Bdc3WUZZsi"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.9_1618569010526_0.7073666490207942"},"_hasShrinkwrap":false},"2.11.10--canary.113.0080e999871adc8c1244160a407de3317d5b2d41.0":{"name":"@sberdevices/assistant-client","version":"2.11.10--canary.113.0080e999871adc8c1244160a407de3317d5b2d41.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0080e999871adc8c1244160a407de3317d5b2d41","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.10--canary.113.0080e999871adc8c1244160a407de3317d5b2d41.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-9iocii2wNh9pNw2lenFXtMLTzmHBrx3F2uKe88poA8d033LYwcvndbsgdGR7ov/8sITLpjPS55vxfaarr/yhSg==","shasum":"9605c5e3aad6e6b9bd141e3288524847e1fb8ef4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.10--canary.113.0080e999871adc8c1244160a407de3317d5b2d41.0.tgz","fileCount":50,"unpackedSize":921009,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgftJ7CRA9TVsSAnZWagAA5FAQAIeGWD4doa2pCNcH+j+F\nXKP1FFGtv/6ObypxQUnsOS0m1L4izTg8ZeQJfUmTVRziGlpoeJQMJPjQQicY\nxbNjJWgoe4GfNS5YT3hk++GsFBp8dGzeA+rUseCWar3SW5gqUqP8u4RrxkPX\n+qLfEGjQC0Ktve9Sr4ajJ/MtwIJNi8yz4iAWX7MNEPVCqbhGPwJpaNLcPbAX\nqO46xKn3nUHLJMNCBvm6DM4buYVqM88ILoVbjjt3a6vgek1VGpepBj2yMMHV\n2noIm50uSQaMf3Jx1B+OcKKrCjXntgadb3zXtJBJC2Un5FE3mHOVvf+6V/1O\nAcP9VIiG4/FzAMBE4ChVe6FFhb10SX6pyHLKt5O4UJCELrhyg/l4AKzQBtWd\nhB6KJf9wcXVeEjRAWT6pwGwOzrcLCqA+5sojmIQuxmLu04iBZ8bpEgraQszG\nO7noi5IiB6S0Y/jkR64IQEnDt4NU2lRkNr/b9z5NfzWCViXkKYoSSxyvMYXf\ntgwW7rPWQBz5VYsBbbllzecXjX8pYKOMpfnLNaC0oHwFaoA52Maid8MFUxkH\nsgv/ht1FRJ7c6xGUmAKUijTYXql455IDX5kU9tNVWhSgShyIavMVC0CfxZil\nGZspJt0BlkxjCevDoXHm0KPO47gxstdvtcHhnCikDiYLtsxdQzA2VQ+YZemK\nxK3m\r\n=QSWg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAE9ra/a86O/0EdXSwFgzQbKrfAwJA3jYSUTTeCvvQ+GAiA9jy/Agr21El3321SAM95DfcUIAFwYoEfg1x3/QhBBmw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.10--canary.113.0080e999871adc8c1244160a407de3317d5b2d41.0_1618924155000_0.1051292828504733"},"_hasShrinkwrap":false},"2.11.10--canary.114.5b05ab435fd4da6fee0cc1c5a8975a0358d0c644.0":{"name":"@sberdevices/assistant-client","version":"2.11.10--canary.114.5b05ab435fd4da6fee0cc1c5a8975a0358d0c644.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5b05ab435fd4da6fee0cc1c5a8975a0358d0c644","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.10--canary.114.5b05ab435fd4da6fee0cc1c5a8975a0358d0c644.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Qzy1TsRsnjnbD7R94yP1BwS7Wjj+6Zaeg/afKTyan6zOnpC0N/lBPXYHuHV8ShujCMx5GPk5XFRYSRF2uzisyQ==","shasum":"c35f3a5cbcac054a53ec01de18507ee16b7c4343","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.10--canary.114.5b05ab435fd4da6fee0cc1c5a8975a0358d0c644.0.tgz","fileCount":54,"unpackedSize":926323,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJggTEmCRA9TVsSAnZWagAAGcAP/0wWVp4LtdIjDb8k4T/S\nW35ZJPd39tSMKyXAxJe29UY9aja1djW0Q04bXZlmaBdqmIPOaG1IuYBMPY9i\njaKzO9uXK0KV0P81CyvqxKGmjmMa4SNs1OSQuoPnYnZnmB5iO/zJOV/HYiqk\nOmulf8HhHuvBg5vYZJ/2fuWevs5wrgFo3e2nTi6RRGQcsEIdnQGgqYOO4ZwZ\n69emwqxuI+cGAYUKAXMcVFgvCuGRljFxOK0qpNeYPHK26lLjT8xPBz7EzgQ+\n6GsgF2MbsIlSwCFgjLno9esnnPeurEN445ZMa90FxNucg6QdO3zTlLOlP7/p\nlYVx16On6xRXjIC1lnxCX5ZcCLFGHExAsaeGBRVbhXocYne0A2P04GiAagoa\nzwLcbh1z64OsotOl+zgQ6Gu0IbvRtqp5WRR4vNVw5ey3L2DSkfv2dnLJ1lVU\n/0FwzG8LZTDFqlh38W2Y9u2Yaj3wNqB2cYQeeymA3ws4GScjn35HonDiqUN+\noRrBAhMhXBMAq8ce2RdYyKBeMt2Zv9/8XMOzlliB71H/BTwM9OdY/jCbOCrv\niTk70bRBIuCsKvn4cr3NVfmPh+MWila+hlwf28W5DBi6d7IYRVkiM+Z3+X56\nHjCJ5XKPEye9csm3n/h8DRl455b/gxCUsbKfGdmYmNkT27kmu9iZqXrOTz2K\n3ZZs\r\n=ZFon\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAbD6WhSx5R+r547S0jEDIx6R6boXx2Ln/44mAfEarB3AiEAq7oJnhHm4NDnanenC19vLxvUncWLdBqo8GmyNzNR89I="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.10--canary.114.5b05ab435fd4da6fee0cc1c5a8975a0358d0c644.0_1619079461061_0.9286356227266515"},"_hasShrinkwrap":false},"2.11.10--canary.114.0d1e8c597b17f3df446acb415a764368fcca8961.0":{"name":"@sberdevices/assistant-client","version":"2.11.10--canary.114.0d1e8c597b17f3df446acb415a764368fcca8961.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0d1e8c597b17f3df446acb415a764368fcca8961","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.11.10--canary.114.0d1e8c597b17f3df446acb415a764368fcca8961.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-wCd7vbGShKCzjBS8Zq9NQ4U0oTTw6AWXw1hKvayq1At+PEjIJjRPoGgyBs6Cp6qSVFjl86WdjYLqkj/348Xy7w==","shasum":"3ef713d2baecb3b2ee9639a561991998fa04d9dc","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.11.10--canary.114.0d1e8c597b17f3df446acb415a764368fcca8961.0.tgz","fileCount":54,"unpackedSize":926297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJggYIQCRA9TVsSAnZWagAABNgP/Ah+gcRViiwChqb6kPOb\nEGAQsCc36NJsy/GjEv6Fw2f5QmzlkFZi5zI7m7VS5X9RbWscofGJGzUknA1X\nvcGXlB+uDkF56633Duiwmh00XEshIdk0pSvC8gQv0hmsAHl7XqIN9+bTQRWy\n77jk+YL38MW7ZxdiP+hjoao6oQo/wnKDbBU9qcz2Fjr27Ba6ZIfAQpF02Zoo\n+ZtxkwRH36JLn2ouRDVlr86Zc4bOaQUuakliaPBwP92FObni7rDjCpiFwsNk\nR30lFOyjzgNlUYWAcQUjA9RiwYPboxfuJTT1MHtyi5HdJVTQKKNCMk4oUvKH\nKYKGjCFskkJtDqs9e0XV+H6sgXRBaWgNX+u4iOw1B0W5LHY/FU53ull0moUb\nmOoRniBt9pLcD+OK+t9StB+eTzH5JhGNyoZAdWKttT9uxPj7FTi1MACV3A4I\nKzkpreQqpVLu6lDeIosAGpskHoEjCVQLCtRg64PNXtfLr9UPCkHyYdbGX+F6\nH29f8Umi5XeHCVUNxCm6wx+zG2wgIiJhqVnEE040ZObbOMpeymD0LdLGCIcb\nthf4CPp92n8RJQBFJW+hKt4/5xQJ8yCAxC63lIy30ZWOO1fNUU8wlzJFLwFV\nWBI2Qcniw9aseK3/h+bJFUkBrDbYIm4m+oN18BejaA3kCwFCrD2ctDEX7JyQ\nAm8+\r\n=KtQy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFhgTddCl2ajPeKYS19MCSVkLLEQ8octRlP0T4tfNCoWAiEAgZCoYo5Dgm6xcwn0nR+HtHgdBN8AYqjbEdzoVe6Qx4Y="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.11.10--canary.114.0d1e8c597b17f3df446acb415a764368fcca8961.0_1619100175872_0.38383268480417776"},"_hasShrinkwrap":false},"2.12.0--canary.114.040ea6ff05e0697ac3f5a66cbf9b8c7c440f3e3d.0":{"name":"@sberdevices/assistant-client","version":"2.12.0--canary.114.040ea6ff05e0697ac3f5a66cbf9b8c7c440f3e3d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"040ea6ff05e0697ac3f5a66cbf9b8c7c440f3e3d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.12.0--canary.114.040ea6ff05e0697ac3f5a66cbf9b8c7c440f3e3d.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-JJDH0Xpor97wh8gvoy6OzqMkcQwUsGFnVojxAhiv+O3xPjP1dZSKahRCO9KSFLJpJVgl1COLHIHfJ5ndjZGscg==","shasum":"f973f9dd5962ec078f9449327af81539a246e0e1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.12.0--canary.114.040ea6ff05e0697ac3f5a66cbf9b8c7c440f3e3d.0.tgz","fileCount":56,"unpackedSize":939280,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghn66CRA9TVsSAnZWagAAkL4P/iw+KxRb/mceV9lKYW/r\ntqcNFhYVUddFAwQ3ck+Y5FAkHJjpdeeG32fEtyiGByK13PSvPD4BzK2aZdM6\nU5JLieGyOrZdaE5bbS+4XrPKYnX1w6ROn6TNSLL5EsFczsLlDibRFPUAJ2x6\nnEoSGopZeKdk5oN1zv2aGD5qIEp7FP/i3QdXHSh5OpXe4qXLXy0ToTMr1X6e\nVGrtgAugU1JQJJTn1Ak3DzWcpOHwmujpPbNy/dYkB6VIsh9bm4TVjGxcjvnr\nvZHKYv5yxzP1s3uVWmi7L1cIDX/eexGoSyM2XysDmGV8B4AK4NzKV4+Igm7g\nsevYLVozDBJG6+80sF/g4WXcmsz9+jyANkE8hfFVHy/g3I3C516V5UKr6gB0\n3TqUo74JB5AcCg0Y0axJ5fW6SszPXw6IA5FOew3C6sXUKuSCteEtoYvv/Io7\n5Mncx6tPzJey55FYkuuX5g/bGlj+YnFo9Oqqy/h99ZvWil92YEmNoa9iBl23\nPawFO7dUxaaKmlQbW63LW2behRWrNn97e/4P1nGQ5s+BOue1aIO00HYr+KMV\nYfrvOlTTxQGQ7mquIHo+cdUAQXapwcAmJL4MJuuico18iUdUWg4ztbpVro13\n7F26tWAuJHN+6asIAwBX4+mZG08q+6L3hOWsoNETMjEtJHFmgFVC4SjwY9ZJ\nvrIs\r\n=gUF+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEU0NbYuSgFOiwoJui75mMTym+r/LF4KQF1DQfR2/PgUAiEAgG8uOa2wItlUzwLoyoIRfxg8R8ixvtzfsyagYV6j/1o="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.12.0--canary.114.040ea6ff05e0697ac3f5a66cbf9b8c7c440f3e3d.0_1619427002017_0.8268609406708276"},"_hasShrinkwrap":false},"2.12.0":{"name":"@sberdevices/assistant-client","version":"2.12.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3fef6c17f46790ed6d2ada64ad11ac8eaca759de","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.12.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-62jASyDQ5szrbgWHg3r3E1MvCJWlusbW7qJW1pGYqaDvTfIs6BllOY/mQ3tutOyKVMYMkbXY683MRQE2cmbZMw==","shasum":"e9240e9e7ea550aaea7c3098d57e3709960a8597","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.12.0.tgz","fileCount":56,"unpackedSize":940212,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJghrMyCRA9TVsSAnZWagAATysP/18BTW2tvgDFe3U4fKUT\nXlzblor308llfypm2cP+BGLkZBUnAddNnzxsnr2C8mzcRU2ZX3CVmOQv/wX3\naDgnx1m6GaBLgOBIf1PXhie0Xx3K733WXz8ApjQLV+9rZ8CzA8w2GZfDXcZV\n2NbmBTRi04PgbRCgrPh/64h0nF3OfJOiii3eV9NMHV5QGwDDW2CIc2Oo94BP\n9mvBSfu3H9RNwZgPLi9SlAoR641bKl7vGHLWJt5am60v41z2UhMjk2f6/Cgb\nPO+JnlbX2VXgLD5iH6Cvh/Vy+6PNZ3whguT166VWQMbePhLncEJGkZYGlhHZ\n4IMl9sxZTAj38/lr/Ny9OpCXS3qjMd0jhVYky8fdqtYYSu+GKD5u/KSH4/qp\naIoYwrXbpHTU5hCPe3qtCyNKeumtxRvTPfGUDh5xZQaeZgVIs8wfN3YIpKf4\nDTPK0+935h5qOP+O1xEbWGHKPFMnCA0dKPNLUHhHL0EJQJAb5YygqhJqxHHT\nu25Fn+onOTZtofzqlHYR45tjf+pvXAOB1NlHEDfZNQEz6chiURbh2jntxr3d\nlQ7DUC0vOW5/5zoyhAZXP3RiJHwbSRnn58l0Y0DuQAF/3OeBcROgsWF1UbYP\nHmIY0Sj7DF+JUPKE49FkQ4aaQQG1LRs1Nn/kqivsG70SGZ3gRjVczPOnDx8Y\nFohB\r\n=Tylh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICYWssLKLW49iuhQme4/7RNi/X7iDysbPC60QAM0F8b0AiBQHVqRF/0WxiGnT78sCyq4nWzD6zfFfUbSjtu8tWsxKw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.12.0_1619440434067_0.7650072283520146"},"_hasShrinkwrap":false},"2.13.0--canary.115.1b76240d499e6bc912bf4897f2173a1aaa1bf9da.0":{"name":"@sberdevices/assistant-client","version":"2.13.0--canary.115.1b76240d499e6bc912bf4897f2173a1aaa1bf9da.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1b76240d499e6bc912bf4897f2173a1aaa1bf9da","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.13.0--canary.115.1b76240d499e6bc912bf4897f2173a1aaa1bf9da.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-ijUq25Nu/LNtub3bdV5hKShCkPSGzw+cUeYfheqAJhi/dB/1GwWcJbcuqDRLbnivT8TdcCmmLQ+WiBqOaeZLJQ==","shasum":"1f26f81931d2558066f615ebefcf5a8f46a5d6a4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.13.0--canary.115.1b76240d499e6bc912bf4897f2173a1aaa1bf9da.0.tgz","fileCount":56,"unpackedSize":940504,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgi+w6CRA9TVsSAnZWagAAei8P/3xN32NdJ9ufuVQua+dl\nxrXuKDhlgySa4QIPNu7LqC+nmwgbgrbrV+JUBPc+61jBnSsSKpL78UO5SCKu\nnnasdKNTtFosHY24j6r6VoRBPYW5U/jJfDao7kUQoa6qzW4fhhw+ab0tzO6E\niDz0ylKmr2yDzX3z3g1CG0sGp5juKrJXCg4I6tkaAiB4JQ7ppf/Hjv1X2IiQ\nsh/L6TQDIzVQ8gK5q6KhE7NFO0ldj6TSrSGVmaHmzajjUWoHgfrzrk80kTX+\nzFvzXKomBDwKKgLoJljFkVhzxmVJSxPy1023jXfrSsyiOLtAmUpv1Ipe5Z+m\nBvYcwXdprn2HdgFiWiY6DPG+Ab4Y3rjig4GwlzwO6FW9fgBAwPSbENrPcCki\nFxKwcfOPckWzue8fMYln5Vp0m8hlH9cbdLNOJSRZENldX8qOYWLoC3v5wZHE\n8DHIiY6GoYgkTeNok42Jn7mpD/c2c3e47zgByR6VnRuRGff2KegEVwxiiAhA\nANs3Gu7VQn1PydstxodQ3vU53eYGkFpmIEbnqqapndC0dXX8+WAyLLBYFK8t\nWJaXCji6TFwhm7rYDymh37o59Kfr24xMNMdQYIz8+teIStft8mviFGlBh5lb\nKOBHlh0sfEfEUXuArAYCmMzYFG8Pbf6d3zfHcY5CCtWVKHCYOA2O7DJv5SYM\nn3Bi\r\n=zn8i\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDzgqrlgOhv0zWw6b0dB4VVxbySh1YFer2cg0/4/w49eAiAO7XKQUzaaSfdmgqUL/YLRrgZvCLadPsSNxkZOE+oChQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.13.0--canary.115.1b76240d499e6bc912bf4897f2173a1aaa1bf9da.0_1619782713953_0.09847641462710377"},"_hasShrinkwrap":false},"2.13.0--canary.115.23ac11b7c1d7cb3e555383e46556cf81f478266e.0":{"name":"@sberdevices/assistant-client","version":"2.13.0--canary.115.23ac11b7c1d7cb3e555383e46556cf81f478266e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"23ac11b7c1d7cb3e555383e46556cf81f478266e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.13.0--canary.115.23ac11b7c1d7cb3e555383e46556cf81f478266e.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-6ABZE47sbKnHfbeRgWhBSfkkrwAu0wS68NvE+1sBBk/UOtIbwLayR86Gw4beyWATvMWOWaNK2YmMwgzyP1mKDQ==","shasum":"f2edd050e4046a6276003a702bb082238f04c6df","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.13.0--canary.115.23ac11b7c1d7cb3e555383e46556cf81f478266e.0.tgz","fileCount":56,"unpackedSize":940706,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgi/FMCRA9TVsSAnZWagAAQRsP/i1G3tljBXZ5f+m5rDm/\n1R7+ViMimIiJ0HOGl53/AH0ik/TTszcIzvvvJE03MMq/M7LnP3Iuf172HA1L\n0AfaszIGo1Zb2am0CAIUp3Z96w6ZnFmyZeo+3rnKRW/QCC2dyGhkEo/zXG2N\njI8VHvB8BsrjPc0mHBoKfAKE0I7l+L7Jx7VWl87S7owBCOtJAJz9cC4ZGhpl\nPxy6kPYAg6QQHE+/SaKsk5CHKZr1VSwkQKbYc6SX6jpCavrVt1hLodrd1rDp\n0I7Z94wuefhc2NvYS5kYEkohwO3qIaFyXbKWOsOmxPRZZdqA0wgF9OvzcpmO\nb3Uxe5lmOzvDl4w/R/ken0cCJW5TkQ3pQCaw2AYlNjutRRvXv1Jk9ZR2lflG\nYnq66uY8dELLzjbdRJ5SzvbJXCZ1CabxrI9VMXFuwFaoUE9pSvd3r02dXcZp\n8an7EzUqvyFjCcT+81ZDpXpnOEU66xVM8N0gaoP/NE5MQfrPgaeT7g0q0NsR\nleQ4UTj+WcZfg5sG9z2mpBXP8HIk9fCBEqePlzUAo9KrNlRW4cnuu751yS54\nQzK8mz09UtkzxwbJvaRy0+PM4We9WBpWBtpX0pnIyGs+EeFwg1+yBe9U3YAc\n84Xw0aVkisn7KKOyAQSf2xQ5DcU6sv6qYfuPpUchy16PuLq4MlP5cXUebQ1X\nmyK9\r\n=zrAS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCQVU1WRGtBYUbD2xAmCWrjejyKALZm1/iZ5C4nEMYhNAIgDemVf+z8AaLzRc15RBf9pKcW7TNlu1qR94tYOo6Xr18="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.13.0--canary.115.23ac11b7c1d7cb3e555383e46556cf81f478266e.0_1619784012441_0.4003145328082758"},"_hasShrinkwrap":false},"2.13.0--canary.116.0c5a14a619419f87151c9c8984b84bfb34d72182.0":{"name":"@sberdevices/assistant-client","version":"2.13.0--canary.116.0c5a14a619419f87151c9c8984b84bfb34d72182.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0c5a14a619419f87151c9c8984b84bfb34d72182","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.13.0--canary.116.0c5a14a619419f87151c9c8984b84bfb34d72182.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-pNg5gM6n5Aj7RXWvaPSFasJSvfsUjW2Y+UYz6cqOmL21guRAwQojuRs5el8mbWBjjB/XDW5ewuOXoEmtAjwr4Q==","shasum":"376a57b826d6308d2ad1f2e599b376956e76b750","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.13.0--canary.116.0c5a14a619419f87151c9c8984b84bfb34d72182.0.tgz","fileCount":56,"unpackedSize":940706,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgi/efCRA9TVsSAnZWagAA+ZoP+QCAYVHPcglw4VBUB+79\nn5GjrABkj89zSfQPG3w5SyEeDPqCsYZZ/JmdE3GWFyTciyL7GcTYoV/guWs2\nnFM/UCltryMX55uCAi28sAj0Rl5yeHQMeT75BsGvmOFHyRfk6mZlpPYCSxpu\nrBACtSFX/Ttt+K0SS4PI556hKS2TCohxQ196A1xQc9o0JWBdC7HSqFhvFA1d\nSFrpp9zYJIios3No5VhvfqvYWtmOtzZXltn7Z3ahUUEhezJxEX5389GoXNTG\nmnXtv+CDW1VC8ysxiMV/IZn/w0UDXZUua187lV/Yz3o3rjsFImzUDf3+IvEX\nxDTkIdqYd1LVjkfClFrCkk7W3qarqo8tAVRHrh5KgIVI+jcdtfsvwKmwmfjy\nDaERmWYOJIS6cfbP/YOw7cARB2p3whrxMReqCRw+T1D480r/zmNnxpGgWVev\n5j1YmWCCwvrGS3d77ITfuaqbWp6AwBlTYrIO58+FdYdVV9422hzO8dJ4JBlO\nfZg8g7Uq7GI0yTR5elSEkDoYwdJ1LBtozqZ7zozTHbekDxxUUrIPtCir30wZ\n1ta0gXM6cRpG9JBVxUhoh/iyExiNx+OJCApzb3g6sWhhiAbsCle9wSCNQ6G7\ngIThWOcLIVXSOvvAFA80miec0GsLpzsYemLV528nvP9biFrSDcBW1Vbaui78\nSa4L\r\n=1yRP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG/pqCpeFNryueahJYl3opc2qI3+9yryHtnaZPo6MOwPAiEA5IlVmZ/WQCYkQFOE22qpFbxwuBwOy0CgcLik2RMJkms="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.13.0--canary.116.0c5a14a619419f87151c9c8984b84bfb34d72182.0_1619785630922_0.3662538043696917"},"_hasShrinkwrap":false},"2.13.0":{"name":"@sberdevices/assistant-client","version":"2.13.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"59468dc0f0e990820cd52ac12e12c4bc68e7c2b4","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.13.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-/dWzqjvk2ds78/Cr0cNqpT4PMjIuMICisqDX7bQ+f3PfbOMa403dPiCEmCgze0Z+cjMcFA5CBuKAz2uVhkyLbQ==","shasum":"307afe59e9e0a28282fa333da6dba1b7942f9af2","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.13.0.tgz","fileCount":56,"unpackedSize":940864,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgi/kTCRA9TVsSAnZWagAAL6wP/RQTjOhXQL+u/l2s6FEt\nwMha2Bn0Kpee2HdXE/PVPxJUv+RfhVsNmyWaoYLLFF6AtTyZME7ZayU6LqdS\nEBRq1Y0KCDbq/BljTcMMxTW1KRq0oY3AMJxFpwU1P4cO24Kd8erciIIICFGy\ngF3V7gJeabnV1fOX9jlKfS8+Bwj6jwscUKNKvpIhxsjAuSosiroD9YUY9qfx\nDAUobfwJC387qRRFm1y8sIuxwoP0lLsCPXC+l0IYd8nRu2NyCAsjeF8QeZe5\nwRbugYS5DRzLravPTKUpfpRPDaTknlaL4rTkMdzAXLmdCvr7OJSI2ZPV2bGi\nKBLPH8UB2v1EDt14CYMvWjqSFK4iJKki+JYmie1m/0ybypDmOi1sz2k2+xG/\nqfxAVh3MtXx62a62KwpPf2UZfYL3f25GNaXOoEzBglhHe8SWPhvG/bcyJPlX\nijiIv10RXNIqy0WqnywYGcOgKcF4lqw8vXsEgEt4FPlAqkcnF6MUHiEtbiAP\nrzXTnSZQXA7fl9oH9NAA5XVL24tYUUpYjLBkvWnJNVia+m7f+RR0I4Rga4Zo\nkLsCbdzFGTXcGdpZTYU8pCKN+jk4uGuMigyMNtRzfjmbiAb3eXgi04iKE3P9\nApNAT7FCvywc8vAVcNv6rak72oo9aeJem8xLKq+dEwfUR9QWQp/cjJpiDk9K\n+w9w\r\n=rrd+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBfNTvgtTc1Nc336lHmVanrkEVl7Rp+E5aDlZKYtMB/AAiBSL57sligfn5VwloMx3l1njjlJmkzdTAExaSyh7hkIqw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.13.0_1619786002581_0.8287241253682385"},"_hasShrinkwrap":false},"2.14.0--canary.117.509a7a4768bfdb07d872c486d695bfc7663992db.0":{"name":"@sberdevices/assistant-client","version":"2.14.0--canary.117.509a7a4768bfdb07d872c486d695bfc7663992db.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"509a7a4768bfdb07d872c486d695bfc7663992db","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.0--canary.117.509a7a4768bfdb07d872c486d695bfc7663992db.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-L4rWIVNoYQemOm6pTongimad7NCz0oPpwq9+4Gv+XQSGyLoMIsGsjUqVWbe9K08mxFIc7Ri5cm2DJKHeMa6YRQ==","shasum":"1e1d9f6c802c73e8ba4e74587d381aa2c0594953","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.0--canary.117.509a7a4768bfdb07d872c486d695bfc7663992db.0.tgz","fileCount":56,"unpackedSize":941078,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkqEACRA9TVsSAnZWagAAtUkP/16RA7K/w6DyduZJv+68\nshLfySOcEGGB4I1TcURKvOHLrFxEmyV8V2vrYqOGXXTXr5mT2jLKJt+c4IUb\nXkFn2Uxu7CQ0OqA2mubQdgjAKZsWWjXCbe4EedzIavPlzn7c046+KX4gtDYh\nQP+R3pGqXvLvE8H3jxG1Y1lmPvDNC0gbT61G/uJdqpAscXLIbZI539cEtwum\nMD4X6rvOD1jQqNR/QvEjJWsgo2mKfp1UWwhg0CXzvcYLincUsR1qRz2695Ew\n5aJuzB+ARMoGJ5BbzU4w4c+BE5oK3ZNwl7EqlyKQQDLSuO4b/T22z7SSIEpr\nHZIe1bVAW0iXVTeUy7biymVHcVev6ijW3ZZXp4j7cRH01GPJZG9BVDXnZGWm\nx5Bim2wDeLYgJmdpCN+DArOS04YCzvNy70LomKSwu4hx+9Kh97K9YMxPWX2z\nF0YgMAYnfLQNIl0LOtNguzgFI+PPLhSfty7PXGA0QsumjfVvcu6cKSI1884O\nx3UfHHLxUxiy8w98H2edFZiUemgMpZxs7d1Bs6IsGyH0okxeD9W28y/OED1T\nXqaNmRJPYWMuceqRuk1RXaG8++2WUufyf21hUL1ryBNgI29pmS/N32x8pAcP\nLuPCP6V/LYh8Uq+RyBXLEMuFyUTfy4pjcyl3s3mnMuGvnE3h5hKVIuD16jg7\nvpUb\r\n=kL0k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDQs5g7trtZ86fJucJrgHCCYfY1Y09bd5S5D3+YdeavLQIgMcqZYztOBZjiClm1hHcFtukGH/PYsIrrPAbK2FNubk0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.0--canary.117.509a7a4768bfdb07d872c486d695bfc7663992db.0_1620222208294_0.30956101330555286"},"_hasShrinkwrap":false},"2.14.0--canary.117.a8e3a60121bd5ff3cc6caf8ef79290d274e7e8d4.0":{"name":"@sberdevices/assistant-client","version":"2.14.0--canary.117.a8e3a60121bd5ff3cc6caf8ef79290d274e7e8d4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a8e3a60121bd5ff3cc6caf8ef79290d274e7e8d4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.0--canary.117.a8e3a60121bd5ff3cc6caf8ef79290d274e7e8d4.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-WphrIRZmY+ccNQ38FE1xenzPr17TxRJg3zuBCtiYj5L4oedJEelTQ+hk5Vk6HB2v9LIc3OTiciPlJ5lvrERoJw==","shasum":"137230062524827930fd8c129f990d0c0a7ad8cf","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.0--canary.117.a8e3a60121bd5ff3cc6caf8ef79290d274e7e8d4.0.tgz","fileCount":56,"unpackedSize":942117,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkrKGCRA9TVsSAnZWagAAW0kP/2pPYnmkjbXbne9t62ZK\nm4B2Mu4f/ShTnQvuWscBe6HPy+VJox9Ndrt5B6Ed3COm0v9CyDJCnR3T2B7J\n1wYob0wUYJHqn1smemqIpaMcXhOXXuFw/44MRXPlybKK8vRqq430In4bXFBL\nTzWnGvY2gpcb3lhAt10YQoKRC+jk8tAmRaXKj9C6IFuFmcG4Tmy4xBCZpkNv\nsPd4rHVkTccp/TyzCNrzAc7RfbNuFLvWodoNM7rH8U/6Wee71Oy6LeYaexjH\nzdOCPloavNwWcPhJQOvQaScDlk9NbM0CwMpCIIUz+HEJ3yQRUyVXfO0wcD9b\n9LdjK0dOiwBFipBsfrnw51InPbYfjmnE2K4N3OUIWO2k4R/vsaY3OY+mMJGF\nahZJ2Ywu4/oDzGMefMMIE9MNzTxtiPye+pI7cuLGcYxaKlZVr8oUz7dNSuOs\nsZ/kFzzEXg6OjssiNSzm4TsDy3/WEiI48V16/qO+vPfP5IXn4oq17r8U74Cu\nqOCNe8yGZGhPk2epVnU4EulpuP/aH4zL0xqUlTR2PBVjgYVsuMxQI2zmnSmO\nFU2D+2uaRnNB0lfq4LCCW29DNy1bh9ArtNHAgYbWN30XA2nb/K4LgoQTr3iD\ncnEtx1rtoB3LlK71CMEk4/gMmNy3kcLpvPPhqW5ktSeiyZ3L/J95uvvh63uK\nai4c\r\n=eXCf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFIxASIekDvkESV/KUq0Vc6RecjuS0V1iI2pufZHnXSqAiEAoCN321z4DL6ohnwq4cswn27Zfj53iSsVnLB4A0hP2jA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.0--canary.117.a8e3a60121bd5ff3cc6caf8ef79290d274e7e8d4.0_1620226693711_0.7932596188585748"},"_hasShrinkwrap":false},"2.14.0--canary.117.8a57d4fa7420789efd3c8ad28a01e477d9734389.0":{"name":"@sberdevices/assistant-client","version":"2.14.0--canary.117.8a57d4fa7420789efd3c8ad28a01e477d9734389.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8a57d4fa7420789efd3c8ad28a01e477d9734389","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.0--canary.117.8a57d4fa7420789efd3c8ad28a01e477d9734389.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-7lAXiz6bKcIL9wDpdF9yABkmjH/BwoxBIoYoE3M+q/ai7Gd1dLlRLkRl6cX54kJ2+IfvM1QQ4RURrktIG6medA==","shasum":"02bd3125434626ed0c06c8c6b519df0319a51b33","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.0--canary.117.8a57d4fa7420789efd3c8ad28a01e477d9734389.0.tgz","fileCount":56,"unpackedSize":943419,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgk3w9CRA9TVsSAnZWagAAbwUP/1IJYq2RFirz3gKbpxK/\nQGvu/GzYvKO1is0Hwi54X3BeWgxK9EwllM0CT14G3EHbmPudXjlwgbgTSaAX\nKYjcrDDb6U66fvAH5MObKKPaUO6yj9wLm0Sqmf5Y+i9c/57BBy8NJIbToh0x\neekksmEa4hLVsyLfvRMJpLtdPPNkSwU4T3QISOR056l4bgcmGneqe7TGzVX0\nf63aQPBU39+LAIhODOjDX/7CX9wC8iNjVwOdCS0PpFewtdPC4I8t4BOfMJ9o\n4qrxpqJW+EZKPuIEycqocWKApQlqiid7460JgCJQHnZZzIwqpTVoSzlJg0Je\nFaHvZ2qVPTjRHN0j3z2z++D2aFqc7LvnSz474XpmUEtvgviBW6OGUADEfasg\nSZBeK8y1r5Tp4qFnPvT2egtN6v6E3QCQI7p9W2E5tuEzZd0jlQVwAa/pyqLA\npPFhtcpZg3vspzQRAmznquLvNC1B2OGJc1C5bnstavFNUwKtR8zHK4gyTG1y\nxEoptEsVrWjQQQ22BT6wp0oezdBqg1bvXdkWQsrYz2vuMDiagqkx8BB2dwrd\nkSKTzEHPoi6voFTxdKsnerVQD0RcnM8yRIExfKRRwRNReUCPOtWpCgR92x0p\nt018XBVPTKkKKegvaGasQs9oHpIBb/2aeWPthwy4X681BeQsemIT7KA2E3iq\nUi2h\r\n=LUk/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDidOO+zpTThoVYQYFy1gYCaSQQAYIUiNEBO0xojXbthAiEAl9z8hiNgB5eEGoQbQQsnZ0OxQw53OfeM6h43pVaKxlM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.0--canary.117.8a57d4fa7420789efd3c8ad28a01e477d9734389.0_1620278332628_0.1228575710828621"},"_hasShrinkwrap":false},"2.14.0":{"name":"@sberdevices/assistant-client","version":"2.14.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"38504836108d8e3c9e85a74f1e11d96ea56854fb","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-nTgOS1KH/zyXuIglfPjZs7ln8eooTB/YNXnz5SAsDi2kabwDB0ZkdGZa+fL0xZnqfbrBXCLPvjkg1IVKT6atiw==","shasum":"2ee05ebeade4d0dfbb1c0ef0da7a6881c97efe79","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.0.tgz","fileCount":56,"unpackedSize":944330,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgk8+5CRA9TVsSAnZWagAAB5QP/Aivi6W6H8qH3mZo5up5\n30a1gwmAF82LJIGWQSv9WAv2Oq/CCFE1mASC8MC5ddYZah8BAOfvq8KTjy+9\nN7NtRU+IXKnsCujGKdDiY9qOt5ylTW6OvezbiAjhvJbck5F2wB95/iU0ORcS\nvXA30k7Ez588b+0clw+Z5jCWO8ZaK2ZCU4xrqrv7jtLMGvqoyZQUCmv35SSG\n2gcSCFxH9RP0rtFx3cei00zh2JgJfU9FmWMzuzxedB3W6jRhAGwv9wxx0IvL\nyS+aCQNFsTQFG3hVp9QaMTWNL93ZPbrE6fwwJo0U3XX/Ag0oYqmVHgOXQX4/\nIRC8P84FWl6TRuflUjTbgmFdQg5LTs932ST+kEAQPo6pkW+axIA8Q0AbcsZ/\nfLVgFNN7O2n4BhSI2bTTah0tov11qAN8tb8mK9kE0zZDtScY8hJjX1IrM8IC\naMAxtbwFvuif2Qj/Vu2wmPENEDGxFT42kCUt4Vka+AkXFsYJKnGsfT/uFY/o\nMeF/CymA2vo/sljZALoD7GPsw/3W4va13r7Phcyhnut9lFeU5onsXUdl+62r\nW5prr2ihd6rzkXOSpOH9+3qCsRnJc9zJDHn1UwkiRvcmuWAFq/+WKw5yxoLM\nehvNzeShY7sVm/JqovJufrQhw7rrpRAHks/gfLeHWfNZtcDA4Xs+S4mR+LJF\nw0gQ\r\n=DXWm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCV69RUxxC9m1EJdA3OnJeaCdQqZnkP8ekkVRs0DUjgYQIgUMAWOr1uy7kuB1XF3ez9uvPafsEKTShKiIS1gO8pUpk="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.0_1620299704495_0.2896747879953241"},"_hasShrinkwrap":false},"2.14.1--canary.118.7b31056ef207f2fe9a88fc885a913c004a7e211a.0":{"name":"@sberdevices/assistant-client","version":"2.14.1--canary.118.7b31056ef207f2fe9a88fc885a913c004a7e211a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7b31056ef207f2fe9a88fc885a913c004a7e211a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.1--canary.118.7b31056ef207f2fe9a88fc885a913c004a7e211a.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-JiIR1Y6AiNjVYIY1icztmde2KSPqkh+ZqxSC58ogNSLFa9uJIdBaYxsUHKrEXEIeMmqS4xQWdxcYT13BdnjgpA==","shasum":"68b0bf5a762669c071c536553195300fad6a9a03","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.1--canary.118.7b31056ef207f2fe9a88fc885a913c004a7e211a.0.tgz","fileCount":56,"unpackedSize":945126,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgm5DlCRA9TVsSAnZWagAA6b4P/1K4jbFiWv048wy3l6hK\ng+4p3wVW7BDauMehaJk+cslwnS56KT4joabrXVCdTv1s/mnqpv6P/myPh6s4\nXmFr3Dstq9a9Q9yDFAUVDPG6HYAHFP8ZPXq4R+8Xh7S5TNzdpvP0s/XqhXtr\nBKmRwpLofwb55CmNQguzuHqfBwETkBrPLvZ74qEG6e/D52bMgNRJo0v/PbIU\n4mHd0UrJLELoQmRdUF5fvxoXCeVgq8uveUTqgyHnI65SPU8hKk5eOhFij31q\nwbSvcW5gYnxBF/0LogCi15YNAejBHyPShiuPXZXSX7/pLr+MryMp60/oTvU1\n/2mKyjpVWcvaQL3URuBwybvLDNcpsztdT7vUnKcgw4wc//d7tvhoWgYqiXRb\nIuPX1hhGatWXEYXTaqrJuSj1G5nTb1PeY90VBY8ZcxTJ2T2bRfHr8vcJWuCZ\nk+TQT/cCcWx3H6a787p5GKjOE01n+Sdz28kMoXl04cDfjSPUGK6ZN6WlDF3m\nSNJ/yu3yVBVJOoEp9bjB2VMO7CIvqJgnRYVFCcAmU8pjUhuDBIZRIyzQkDvP\n0n0q8zj1cXKuIzVtUPc/AQW/fXGoGzi6Y/uKE6W8HbUpaWRmz3hdEKANsgJw\n3WRG21xh2X4mzn1NUhaALcfs5YhOLyIgYf/qcrd00Jfl2NFW/H+v7TWCt+v+\nGzpT\r\n=nI0z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGu0Rb5Dm9Z3KS4izJ5AyngAGHjBoq+/1l8U4CGthh0NAiBRb2UdQ0C+QqYljE6lAe7kVsQlrqQeGJNgW5Te2X3AbQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.1--canary.118.7b31056ef207f2fe9a88fc885a913c004a7e211a.0_1620807908753_0.5578395467280148"},"_hasShrinkwrap":false},"2.15.0--canary.119.2055ce7f5b0fdf706a3c7fddbda9e1cc5a4dc91b.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.2055ce7f5b0fdf706a3c7fddbda9e1cc5a4dc91b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2055ce7f5b0fdf706a3c7fddbda9e1cc5a4dc91b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.2055ce7f5b0fdf706a3c7fddbda9e1cc5a4dc91b.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-1szV/Vgl9H3aVURyiOpIcqXuSlMxrMJSQATe5vvpU1YUHR3kBno8mpu10D5gcr045S7UaE5t9i3ryMiH0LwKXA==","shasum":"36f949239b974a9fc01b9237abee78d7addbdcd8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.2055ce7f5b0fdf706a3c7fddbda9e1cc5a4dc91b.0.tgz","fileCount":56,"unpackedSize":948506,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgm7f0CRA9TVsSAnZWagAA1qwP/2GET00oCiWSy1CXQhrF\ni/Pu3iDKjPgW1R5daflBzKf5Y9/3aFswpLlmqa9roliD389oB/J7M5sRc8N2\n3C1BgUIksjKVj4fdE3cu3O7RqIuGdTdYp46KkkmDpZjthi1x5ECGq9MRS84f\nBqwjLV2BpgItnE1dci2i+cTHRvbR4E3jBnH1yJ5ow9KKaDJqHocDVjWrWYSG\n13k0WHkyIiv3gCMe6qs5ULc9jZ64j602EH5ndrWxiqDakX9ErmN+ZQEKT6Z+\nZoNuH9WRMrWhPpVoYDtGNxETSbQEyRr+doDYKJiqIhrZ51igVGkJfW3zU91z\nRvYDqfQtoeGkAmDdsVv0B6dMzguEv2+gRrTsWHHyYIpmy4GDgEAjnnVneZy4\nxe6YuUkvWvu0sFgYi+8YDUUkK4TVy6Ghf8ec8jTMFP6MYFHQVIw3tb9N2E7u\nGW9hHA1dNp031W9BOnikqkB64ZLrqu2Fmnpd93TAWXmuZxodgcUllhIF9Obk\nxq+OH1Kf8yKF+CNCxVZp5ryzE0nKSbe3FZid8icC+f5MIrkjpQsRe5ZDsYSJ\nBuDrqqxQDERvQn6PE1dh+OwtnDrEngx6o11sFun2UZAARdXRzNWhAeMK1+3z\nUM+vUMpHq94f/GeggLJYg2Y97HSYF2BGTBo3bfcJwiFRNJuFcA978QrdII9J\nZl6Q\r\n=c1VU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCRzk/wAY8zhrhn9VgV0bBxzElbu94yshn4zDt7OXmaTQIhAOY2YSYUFwTPiJ/2MbLg5UrAhUcxjJUfXdItJKjcukRT"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.2055ce7f5b0fdf706a3c7fddbda9e1cc5a4dc91b.0_1620817907676_0.11585990108598576"},"_hasShrinkwrap":false},"2.14.1--canary.120.5fad138fe2baec0cdb96a2c6fcad3190222be449.0":{"name":"@sberdevices/assistant-client","version":"2.14.1--canary.120.5fad138fe2baec0cdb96a2c6fcad3190222be449.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5fad138fe2baec0cdb96a2c6fcad3190222be449","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.1--canary.120.5fad138fe2baec0cdb96a2c6fcad3190222be449.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-3YiE4kJPkKuBG2LMQgnUgJQydzuPIJhgnUZb1A4RtyP9+fC1FbJUlsbeu6qlVrxyPnhJso021Zv5pUupUV7sMA==","shasum":"8c6003dc71eda6319d0318dc3a5d140671f2a213","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.1--canary.120.5fad138fe2baec0cdb96a2c6fcad3190222be449.0.tgz","fileCount":56,"unpackedSize":944395,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgm8TzCRA9TVsSAnZWagAA7SgP/3iyq20s1hQtTx1/7XvN\nekzi9ZHClDNyn6JJC0b0FgcwJ+pcCqtKfVVZpB/LwfToQcELx1VXbCtz7UyM\nfl/WNlxy0FUSLUC3nOG+++xe3CfcdhA3iWyrFg6VCxJcreSCaofBJrcmGr/7\n71pfvosSkoaAeEX819YEbP5Rz25BTsJRnqbqGuaJW1GK4VjO0liE50ZgdMVh\ntzdmEuwXLmD25Tf0GBxacv65SUiLoQxNc+G9oIBSMxz87ku4c/b/5Akb3aOd\nBLuzl79EyFiW5Fs+7HP+jBVtBge52CQLmG56o0lJ1ya1eVX/hcJbEvWlsHpH\nwk2GG0SsEedNo65osQYTDmZL+QL20Iqgo4F1c30L74KwNj3yK6F/KIooa0d4\nWIlBpwr2pemEY71Mcw5QeVKHRQcC5/ZIXoP/PNBwvL9VnhbnPzh1jA4wDixN\n1DTiDWIIpnEY1k7K66OmgvWWHpxI4wLU8HdPfM3d3X8vCL6rUBbLKt9Gk2Sh\nGdP/DJ6n7+Gm9dUTkH1XQTgyh6A5fDghBlIsB4Jkc1/RCE+uBrBz1hEetStr\nsADaTaruB/VGem05e0ZlV8EEzWRXH1Lxh1VHdEBNmisRItkHkqWz3rT7oefl\nV+lL/YUDvi/m3Czxzq5XvOCrtYDXKOOMg5FPUW2vpyLltXG0uYubRWLZnGLi\nrQNE\r\n=/I2A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEc+odCbGETLCPMVIblDVTU+Tj7ECbT7cDWYxSYonkXzAiBIgwi8qqgF0LYBFkEDXcFZP6zIv/F9dnvt5doTNOT7Dg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.1--canary.120.5fad138fe2baec0cdb96a2c6fcad3190222be449.0_1620821234928_0.3925187340183889"},"_hasShrinkwrap":false},"2.14.1--canary.120.185465c785f17011ad97727603b981b861492854.0":{"name":"@sberdevices/assistant-client","version":"2.14.1--canary.120.185465c785f17011ad97727603b981b861492854.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"185465c785f17011ad97727603b981b861492854","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.1--canary.120.185465c785f17011ad97727603b981b861492854.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-2iRo1DszqpdDVGV0Qf3ggGw4oJ2p1zL75aDJaJDfTPJaFVYKE6je+OWqPxm9Fz+bd6yVYcSGYC10gSUspqApCw==","shasum":"65d5668bbbfd55147f6f68b31153c1c284a935e8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.1--canary.120.185465c785f17011ad97727603b981b861492854.0.tgz","fileCount":56,"unpackedSize":944638,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgnM5SCRA9TVsSAnZWagAAYeQP/RiNOsWiQ1zU/TzzLpK0\nlidFfzmXoWePvKZKN8dLHMaXNn4tFrLLR4AX50dAJSGnqKoEzxsBUci2kIIp\ncDAEBU+B+lR5yzfAT+BY72Zpit/L6rbOt5XBtx0mfg4gzQsyxmeLcYPRq7CF\nOE5TavS+J/1rP1Nq5+JewkIRotmDin9W9+C58/JmOZHvgnKx1F6+yBrLw4Ss\nnxrqzww4VR5j9a8IoU6iaSKbq4uPzd5xLUCsDm+tPC6hghSaj7ityHtTkAla\nStiCr/uvJrbuG2e7ryHxmJb6vlrqlYZuaYutxsgSPvRMHWd7HYOi+HEPn8Bc\nbfTpB50TMh6abH/p8Ucq0rh9jMCrjsN+Rr2cwfgwKQau4ICaLgr+iwwLQTo/\na50vRIRwRpmkcSOyUZXkV0h/M1B9MfgxL2lpgmjfe8tWV/QdfVfRR5xsX/uR\nE65VjGuPCvRjWgobjdB9PtJlT3TwTlGLEJ/r9foSlcUDZXUALxiypznc7Gxw\n3bvpwb6YIJRsx4Tu2TYGnxTHtoTng8x3ZVR73Bn8SBxgHXovwHRv6o/+LFjC\nq+i4ox+tutEZPMjffmXy9ZJdxYV/ipEqJdRoQ7cj15r23MxAHO3PYKnn7YFG\nTzByJ1d9r6jVyT890JVVqCPxTidxgsawd08aFLAUa9ejbLyNRRdeswZKOBo+\nn0tk\r\n=SxJ6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCODRuRJ3LumvzR5plPGST73Mofme4pwwgdA5PXNrTCSAIgLvdJev4IOsMbW4qcuFP0lbaac8lBX5iKPZEjfD/sq0Y="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.1--canary.120.185465c785f17011ad97727603b981b861492854.0_1620889169333_0.37001091345150283"},"_hasShrinkwrap":false},"2.14.1":{"name":"@sberdevices/assistant-client","version":"2.14.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3215f069f86ef17fbdce50867c4f2916862ef6f1","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.1","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-1EUI5mU/NwGolBlQ9FxuJcDlhMWOWYBXlfnjdqYdEMOPqbEGSX7zir4ifiJq8bnDGi/6O3/1TaTtzHQwJdCyew==","shasum":"9c25dae2f8d61e027ec9315e37af3f4acadab596","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.1.tgz","fileCount":56,"unpackedSize":944781,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgnNCSCRA9TVsSAnZWagAAtf4P/iyfL5gEpzxImvil3QBt\nxqVpctuMSVZpOTp97Z/KY3K1SZbKK4mPHjYNNu+sATmnjoLZ3TfYepGfZ8qr\n+DrkBw6udVA0NHlG6apjf3CTAckd5uViykocao3ofCK4Uca11jAwHF6yRwK9\nhe5jDAPbsq2GpbQZ/oYN8ZWZUu2U5d8pK3+sPDREwdt0dz819WRZrxk7Edqx\nAoQ0C6YgUIHAdlAUvGQGACrFOuaKPLut3axP6FmsYtJYr9MsIaSA4Bq7OUqT\nTLWP4Gu/m2FVLlam3rrK+8biZjb69vMOqhN/d89gPRk3kuRgYd00klYKdvkv\nKCF8Lgko90equG1Gol94ebfN4VO8Y2wBPhpz5vJgxHNms+U9PVj0tohrXH+E\nHxD/ZLWBf1xwC3UYB7Pzb42XeMHjzXGoSSLsV49hNG+hFS2KhFDpBzIHsRo3\nBHM6nSRouRu1TXe8M7NcLtT0h7SUlolOxpjYP+o5spXoPm8bKS/PcxQMcD15\nsGrh+YgicVm1S2zhNQ4DpU+GZx6QmI8DTQAsDLqjw68O/KMT20Cyqia1lt8g\njnXwpjUcGtcfHuT9eD5nhurv70Q1gt7ttt7YzyxMfPC8paswdYmNasGuwaOj\nQ7wFfJLwzbV+UVNYh7qkn9xfupnpF3StcTvfV4mHayZj1vT4eOpn82SqYCux\nQkOY\r\n=b5Jy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEE2tUxUmUZucJQlVwER3YEc5wz0bgc8epNsJbNuzh3XAiEA0kDTxNx09hT0r7/fh0PTo8Tk54bk0ujRLBFuTYcD4Rs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.1_1620889745867_0.5945793086396185"},"_hasShrinkwrap":false},"2.15.0--canary.119.0da442699155b4b93da2387831321a5b5945d2c6.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.0da442699155b4b93da2387831321a5b5945d2c6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0da442699155b4b93da2387831321a5b5945d2c6","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.0da442699155b4b93da2387831321a5b5945d2c6.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-6EknXwuZc6of0UvOrGA6oN0SSqwcpG3ntpdWtXTrqmV9EpQCzc90Ghhb1fdGdedmTTBI+8sJe465oh8/vrlyEw==","shasum":"44c848365b8886a8cece8f66ccd7db549d6694c1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.0da442699155b4b93da2387831321a5b5945d2c6.0.tgz","fileCount":56,"unpackedSize":948957,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgnlnpCRA9TVsSAnZWagAAa/kQAIh9jlYynugNrhwnnQuc\n4lWEzMIl/lqxx1PXHyf9PI0WlmspEgYVKVLqtqx+XV5Ej0cfF/2OyQjBJwMS\n2wK6NxBzCqab4x7BNydKLc9oY1Xv5SkAllEbAjYbcu9LVtojNq9Fi8M64ywN\nyNnHfCPJ1MCbxBVjXJXtzqsd0MSANaYBjM1Ui/V3EJ24DFyF6wFCaAUpEhUl\n3WpxpppU1PVZM2fovnLrjrTxvU73qqhlnX0wk9z4FCTjRyR0nn7DnZjLZ0te\nbwHV3/bgdl7R6qOc1fk/MBp6a8uw9GWUQTpeYSbwGyswOHhdPOIcNLqxHuPA\n5zU7WOtjxG+Ffw6A/8JL61ai/Hz018tPS1aMEO8qsoNwSE7vQqY4DOZ+TLIA\n20cREWAg32dlODc7O4aVjP5TZYfrs1fh+xqwSgZk7UvNBvuxf3Y5jx1EksSo\nYfY1y7L7kanRXE6BJ2z4Gww1Fz0M3o4VjNEHqGGKcXUoL51/wwPxqgadIG12\nMoAS9ZJNjMR4KVleo4qEYiwyRa5iGCjtaj30moYZ4S8bw77Fkw9PJxD4sClm\njYACvffK06kI1MITOgE7E37QpEKvqzLgn8SLxplWfKcPD2YySZhmImioryUQ\nMOJGtio9o0GbHhqzxa77Rke7GmtBDCqL0S06vgSU2YiRNy//50kSJjDjAE2v\nd8b6\r\n=Qgy4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCKvqS5Ix3T8StywAJwg11gHdd+1ZoWTncFOIT37DNsnQIgUfe1+teC/LsfeeTHx2z54dbRX7Hxw//98EsuHIwfolE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.0da442699155b4b93da2387831321a5b5945d2c6.0_1620990441083_0.6132039305891124"},"_hasShrinkwrap":false},"2.15.0--canary.119.d80f461cf3606671bcdc6bb450f559f407d15ccd.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.d80f461cf3606671bcdc6bb450f559f407d15ccd.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d80f461cf3606671bcdc6bb450f559f407d15ccd","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.d80f461cf3606671bcdc6bb450f559f407d15ccd.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-jh/QRnyRlMKU2wGFc4IdO70g9XGnfGBmIfPpdfKZyaplqLCZoU/HvSO9lP98BfSmhE78wGHHJ+Uhjrb1tlCiFg==","shasum":"f45bfdb244ba74e251390c8452cfc9879ea65291","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.d80f461cf3606671bcdc6bb450f559f407d15ccd.0.tgz","fileCount":56,"unpackedSize":948979,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgnmDeCRA9TVsSAnZWagAAgYAP/juat1Pcb+/E6fhjBdqe\n1tsO6DKP6b07+O9LL/JJYqW3teZjUK9f6TFoyM6zUBnBAx7vmq2UdDMiKQXG\ndaehdl39TFXiE/zavcUA7oqy6IWaEFFQyyPSP33HLukiWgV61xoSn59g04k4\noam6xChxUa1gQYPMQdivCtDRav2FsxQdJQLwXNow1saBkmqBkzlenSbBGAHk\neGCea56g7U7/3lm5JGQFzDNBK9HTsi6YA/hgaFB85B5aSF0fO6VBYXPq8ygz\nNNgCjfMhkAhreu5LLp6etRbvPZP226mltY6RfIEfT0dY7cEHraVhu4gvL9Gr\n/x5EvJpUCwjSfDwOfcX9WZAAzsSfcRzc4JPByM4GMV2imUA5wkQJ8Wk4R9RP\n9iAgQwNaVeoYKDMLNz6P/KiP8yYQGVnpVXd5QhXUmyzvXmCZEQ9hqx00O7aJ\nD0TT6YyJUlyeJZcOxMA3tlwmbyqq8CiUCZHAnPIHFRYYWis2v9g04ZTnHqUU\nJUdMJAwfs+5NPzWdP8GPdMRCM8PHdN3EEv/RHdIzB76Fo5LXrfkk68YifZU2\nuvv0vFdnHr0isigVJnKkSt/eyG0zCwa7TjQplA6YGezN5WPZPATX9bH/72Xg\nNXYj2Pwb36XOwDVAXOiHAVa+gfbu51mhd5DFCJ7aIhJaBt7fezh3LGoBkkCW\nMQ7x\r\n=lp3f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGiqYmTEMsiE233YT05uhQ93ax7x/IphegRQq3HRq/1JAiALN5a0zqWrM91bAS4E3aOXWmRDKNKzMRioGlQwu8u8Dg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.d80f461cf3606671bcdc6bb450f559f407d15ccd.0_1620992221672_0.014106673448282647"},"_hasShrinkwrap":false},"2.15.0--canary.119.2c78b051aea19898a82d89fb430223af41b27b72.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.2c78b051aea19898a82d89fb430223af41b27b72.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2c78b051aea19898a82d89fb430223af41b27b72","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.2c78b051aea19898a82d89fb430223af41b27b72.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-cbItI01m1/dl2aQv7dLONinfnHtGCjHAmnfqMBlOBTi8gxmHYy7D4vgqp7/4vxMuFpHlDaAGZGtuH3xku9e2iQ==","shasum":"df7c37dda850b5822097a5695146a242a3aaeb9d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.2c78b051aea19898a82d89fb430223af41b27b72.0.tgz","fileCount":56,"unpackedSize":948993,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgnmLdCRA9TVsSAnZWagAA7ZUP/2uy5bheZAqdMz/cqvI6\nsr/rB4ARzGhN6Q3DBceFJlpyR8vMm0oY4WaFVXRtybMTcrsZH8yzdCgg9acG\nPvOckRUt7G4GNpmzIIrdVTTnOsGldAps7sQWRVowFwFsbkNovredkvSm4xX2\nuYLNwioha7ww7DIVjkVKxDJktQvOD5x0P24yA+P2ojGeEoGGVhWgSF/r5u40\nQyoa0jCyplMfY71yri4ae+21ll3WN1Ef2yJB6JxYK2KMx/9K8TrymItOspmS\nZEswXkCymcMoGais2uE9xVy9YWNAyvAxxICbA8nyMWgOZglR8RPt+0DbSPJH\n61K3ru5C8tm9g5gW0Cnstk9lMqlHCKx1H64DwnJjGg1aLXYCIACvuVKiZaE0\nGr5IjVTuMAo+fuCUi1HNeXUFJoaS/bO9/tij1isIV14Br3fCeBSYDLMxvSdg\n6LXxh8ewxuFNI1cjAFKH3q2zbSgK514sFhsiN2iAivwKmOs8/6RCpaYJ5ouF\nMTfxTeDqyh5VzDEgMmrFdXnudBmS21oUcQ6nLaVyxV7n0IwcR1t5r0sXHseb\nWXzw/XW3k25yBQG1x3mhK7DZXe1kPW33EA+xTTbKBLNqPDxL/AfeBvyO+6PK\n8mzvJ1mscEbZH8HK37ChRLnTLjk3wWgfSG52wB9ZNQAnMfBzQ1oNRVPvF5cF\neFW4\r\n=88xt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDdAYM7fepvMoP/kdszQneT5+f4IPrgeCAIjyUE22M9qAiEA29oYGGBRz3hDb0usrr1HVbHDqY/yxSkMAnGpOHlPf3I="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.2c78b051aea19898a82d89fb430223af41b27b72.0_1620992732610_0.8119833575830644"},"_hasShrinkwrap":false},"2.15.0--canary.119.e4007f5cc6b9c28f78d3a19ed11f5ce0b484e8ac.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.e4007f5cc6b9c28f78d3a19ed11f5ce0b484e8ac.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e4007f5cc6b9c28f78d3a19ed11f5ce0b484e8ac","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.e4007f5cc6b9c28f78d3a19ed11f5ce0b484e8ac.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-PXuSkgy6N20LSEX/pQD5gCUObgNOF2vPydQAsLN3pilqsmCGWRKkl2tbkjqfwdIADGtmWRc6wQZ7sDHLZJHn8A==","shasum":"ada846cc92a4b63ff94b75518c894a4c42b2365b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.e4007f5cc6b9c28f78d3a19ed11f5ce0b484e8ac.0.tgz","fileCount":56,"unpackedSize":949025,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgnmQ4CRA9TVsSAnZWagAAcCEP/1oN8N9hK921m4vkzFwK\nEvtroX10N0KYyDc1/y+inrdmMDKjZmm1OVKrX+AcxiougKZ9YfC+S5aSEurh\na7drHaemlDuvlOGP4JKTjstwju1C/yuE1wpddmyrsYNcGLYsNr4ev8NJiW3t\n/hvxiDFzJCg/Wry3uXI1cxoI1WdfAbynuKJ4kQTF2qbX5w/9WQfgPmCxj3k5\nZnpZUEh/qdtDZG924eWD3eGi9Ge99X5dpYe+dwuFtZk5giyV+5ddfyz9lVs6\naP54DkuL5bmjiMZHLKI1yPYpoE2Z8RHi3TwyALuFq47/K+KnGGth7OguhlJt\nah2JDR70qaBqVe0G4OYAkYL0v8x97GlxURlQEJkvHskQit1wY0Xz36FmIvQP\nPcHHIY/Idmjm3a8svNWQaiZ7x1u3E0hJ5gF8+wquIOSRjXMulEaPXX1xOB2O\nfkrHesaQ9OHI5OKFW7aAI10sOuFZY4IZBVIw4EsCAN1ep1PsOQeN4yhsQla7\n/TH6VeQPK8nl5JQGaaNQsjnSJZ06NjsjVvRwy9UgpEL+CVBKERoe5toCYk+f\ntEHRXA0WhSJzplp3oVoImNskYey+hwLW2vPk6E2gyTEtdvAE/QRz7SuJ1uiv\nm9dkoxb+NEsRd6m59AKz6S8Z2a0j9NceSf9ldIheVTiakXCpAdiG1Z9Bp1oU\n2qyq\r\n=OG54\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICkFCGDlu0MnEKQJ9DtmtHPxVahDq2KMzJtT1bCMT0KRAiEAtcPgIHnmJyisc1sH1IlZPRllTaZuGjyUqmAZFRb2Nic="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.e4007f5cc6b9c28f78d3a19ed11f5ce0b484e8ac.0_1620993079651_0.7421930594073447"},"_hasShrinkwrap":false},"2.15.0--canary.119.ed8a412a1fc4e9e86858de85493e748de55a8d3c.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.ed8a412a1fc4e9e86858de85493e748de55a8d3c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ed8a412a1fc4e9e86858de85493e748de55a8d3c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.ed8a412a1fc4e9e86858de85493e748de55a8d3c.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-ZF0p/YMEfM4y/c63jObawDrFWPdl1A7La9I9H7JwDN8tbd2zRNIh7PgOYnxSY1+IypL101vPL6eVLCdwVqAYFw==","shasum":"78d4ce5f3c64cc6f9927b94a10bd26050d292f20","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.ed8a412a1fc4e9e86858de85493e748de55a8d3c.0.tgz","fileCount":56,"unpackedSize":949805,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgnnjyCRA9TVsSAnZWagAAmHgP/jxY9j3FW2/UC+QE4mdD\niFBKDptJttGxdOZfXdu3PBBPJwaBnrbomhL3r6u9AJUSPwIkFE/1EuGVnodq\nMmrudEbwCQ1r+7qvv+lcPEGa46TxXQcIX+h89hPb4hNgd257hXKe1HLaNMRN\ngH5SZBckA+BMAlQ5eWOSn/yJ/ZRcjfmKmf3iCWBUuwg3Ah6OIBNC1ZDBIhfX\ncnN+tKYzd5dwjgA/Wxyeo2vSNKOz1eo8s/9KJdSYVdHkCHd4HnM9NStVEo8o\ns8oDn6efHD7EcniF3Tu1kUb/cnnHniZm+1zY3yXqSFztt+tFWdmqil13I12a\nubr3uEFjab1L1+yfsQ+M+pF7jS00K23tEFR4pXhYl2WgPlpaKOMLCb/mjwLV\nPavO1PdDGnHyge6vdBCjL3RFA4jaiX9J16gbIQteqrpXez3pr9Lm1IvNjtJK\nKjLVNb3giVc+ERgh94lLmi2FWRYoZXtaLTP9S0p3eqeODdVgd7efWzPAWUry\nY72dNPyoWicYFh5M9eboRWDhg3QjUdiiIPY69z2r13rVicV7Ove9pcB1u7hp\nrRDF/drteMUdtsfKWeX9lrYVQ9Jo5Gtip0D7hJy1KrapJ5SkU6lyf5LMVrTa\ndS6kSpwwjPew19zJa1820CIzfcGO3fPe8s+zCyyVbAusUTapyQBagu59WBGn\ne6cG\r\n=7luX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAiOQIIOWTxBArfhjQw3SWqjge5Y5NU74E9SHowXdKvUAiEA98r8aTo3SJx7lf+e7xBrA+v/RMgk/usRkgxBxWaUBgA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.ed8a412a1fc4e9e86858de85493e748de55a8d3c.0_1620998386334_0.6144785067829077"},"_hasShrinkwrap":false},"2.15.0--canary.119.f3b3681d89f682925d6b1d3d512eb60c5ad8df2e.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.f3b3681d89f682925d6b1d3d512eb60c5ad8df2e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f3b3681d89f682925d6b1d3d512eb60c5ad8df2e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.f3b3681d89f682925d6b1d3d512eb60c5ad8df2e.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-feUFsHl6JxpyJ7aZBJVBvBFV7ihVseL1frIh4NtzKpdyIi1h4ZGZxV5PcWaoLSZO00Jk8xajSCMx9O8lSK66aA==","shasum":"ba15e02be3e16bb9393e383007b76197dfa5a5a7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.f3b3681d89f682925d6b1d3d512eb60c5ad8df2e.0.tgz","fileCount":56,"unpackedSize":950384,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgn1FXCRA9TVsSAnZWagAA4koQAJVdxQFAZohZQqonkCkY\naleNkmPM59+fn/TpVSx0QZA5Am/hlCK/RyNABnaaK2ps9Eq1FIPk5Lg5l4IH\nXDqQICtUxt4vxZn3pKHsB8ep6x8jNi3NSKv9XNtyEXLks4lBNAt3IQ4AKyse\nVn8npZR4fmNIKnihK3nIVCQROH+KKRnBqYSEPnRNiYTG9Pc3RBfCeLnhJQWV\nx6rbNNQMrV2bdV9mouLOsYCNFL80IqPby39IIeq40ySTjM2Z+iGRMX4ED5JP\no2BWlABRFG6zV8C3zsM1bxoy8BVG5vYqWjECzskeXbHR/ibN1Eh/GJ+6zW5e\nEbemoIbieCwH/Fhj8jkQ83M3Rp9O8yQ1jvDhyLKtVeVKPo+7Xl7mV15Z8Oyb\nVzQy0J/FyL/qzj5AntkaTNwGmCuGMx+DDzhdeU0INZwRTT65Nm/SidOk/+mI\nkXuvlclM4jiDfbD5XFMXHB6TQQxgo4JG9ot3VScMx/8UDe8vNjiaFhTJC7C7\nUIKoxM0XteevW0TmsXL9CrDZKUT8LGPs3a3LQIZpDNwytrhriKQBYpCB5evy\nPjfNkRqVzihGta+ZD3nYcrT/hi3ToPSn8WQVyg02Cu/L+7FZK1hk/F7djUwj\nOulMBNOquKqswrUOC9Qfosc+d3qzT7bgT2VxCtLjv5sXzGPDiup+LIkbiAWb\n492O\r\n=bgTP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF9jnxZu0U6tX4siwvceetyZL2pA4Ucwowa5Q0WEN7odAiA1sGlmAF74eI0rNkZ/yWjNoIcCb7lsmu0PXdiJY+ULZw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.f3b3681d89f682925d6b1d3d512eb60c5ad8df2e.0_1621053782834_0.3325868920494035"},"_hasShrinkwrap":false},"2.15.0--canary.119.ea6a89ac161f9bbe8a4fc01e09d047a0b33cb5ea.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.ea6a89ac161f9bbe8a4fc01e09d047a0b33cb5ea.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ea6a89ac161f9bbe8a4fc01e09d047a0b33cb5ea","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.ea6a89ac161f9bbe8a4fc01e09d047a0b33cb5ea.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-5JN9ng6c1KQWVxvAor6i38zfW45aW8ruTAhrtN2Ea4nt8o3OIjzTNGpRVmgeaL9x3rVvdVybJXWBaJ9VEs9exw==","shasum":"8c1bea09a8b73b49f7492654893900e82ae2771c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.ea6a89ac161f9bbe8a4fc01e09d047a0b33cb5ea.0.tgz","fileCount":56,"unpackedSize":950403,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgoganCRA9TVsSAnZWagAAcmIP/RGyTAC058nD2HMaTdFD\njQfFRc6rsvDq69exQS4rONZxwMmDK1f0tGZcuCesQquQYJTHGguBFMJ+RvAY\nqm+s8GfnWvFgZ69yF78iqEnTHJc6OzhZ6UIROnJkIoujQufOf4htkiEYUOTX\npHPlr4JEABOO9BRuAaP2Tm3bu3zY0wAaAX/59847LWSK6O9Zkv7RHiGyDAub\nNfHR52y5Ra9F4u6jb5RC7Bucmhc5X6VKF3PQOssQLHcIUmctINt4V9KFNbDy\nPlxDZ7hBEVDVO0UGJvF9tv82wSy6dTFm5VZmI/ZUc9QAG0V+zYesTM3tLsM6\ntF3WlXug1kchklQY+zg6AVjlvgSXNOXssrVHEZGUrFmUcucJuLyMwu5MzSRM\nF8hmpxWeL4fZz48aSWP+N1SYWUtEzVdn75Ws+uVOTtlB30viBeOnci6B01PE\n4ns3R3vuzxUa6DgrXKLWazKkoWTe33EaRDsTQDYLu/HURI7qllB/h4g9NMYP\nCI85JOmpANzw94gGAl0Kqwm6O57+Dib2BBNTKiFqqdYYWQ7Hi9e5uDd7/iiB\nusF0NGZSI9s3HRIiKy+XwD9BojPY8ZdKhy4oaSVA3oBY1wyyREmqNM4BdZru\n8mu36bVb7OuazXLoEU2KersAN4h017O/qB/WQ/Ehg21jsr6i54PqOOwSRRZQ\nX+d1\r\n=LxcE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA74FRgzaFzqqTYM/mL3/6HMnobmnJibH8HNpDR0jq09AiAZvF4CuY+1o2AoYBVSXBptLNBKz9oMVpWq9Z7APQc1zA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.ea6a89ac161f9bbe8a4fc01e09d047a0b33cb5ea.0_1621231270688_0.5873949101656419"},"_hasShrinkwrap":false},"2.14.2--canary.121.182f8dc43e81e6093c735822033f09354062dfb1.0":{"name":"@sberdevices/assistant-client","version":"2.14.2--canary.121.182f8dc43e81e6093c735822033f09354062dfb1.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"182f8dc43e81e6093c735822033f09354062dfb1","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.2--canary.121.182f8dc43e81e6093c735822033f09354062dfb1.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-INjg9Stsxd5dq7UNGnW8B6Wc+X8bQwtvfpK8lVtehWGtK1TLe+jIZllfBFjr0ZUsOPf88q0C7p24bOhsFpHuhA==","shasum":"80ed29973cf1877f3c3e3312758f3ada9cd426fa","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.2--canary.121.182f8dc43e81e6093c735822033f09354062dfb1.0.tgz","fileCount":56,"unpackedSize":944982,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgognECRA9TVsSAnZWagAALYcP/iLVr7lWAgVyBmbLI/nV\ntl1ppOCJ3wBpaj2ufQQa3DtDbToU4C7F4Y48VhetWm91MrlaN1Obvhov0tH7\naYCtoKHEAIF3eMMF6h8oH5XzMoxHYDxFjTFybNVYE0du9uURXyl2cbH6zDx7\nN8agSdD/+RNyJCvRRt+dIJ3xWHrT59wHHh3+ybyCK9qAy3NJElBxVyd7Sjbi\nt+WStbcq3Vme6DhPQVNwjcu1rLWgM58KeM+YD8Y+eLmJJC77qcft0A3TUYeB\nwdF075e+BiiBjiMvGgigGq4GX8Sb/ApYymUQz4Nl1HCJCNnhitFH0+8VUHr9\nIT2b9cZvpSaf1haL16PUraMHWaIXXxQ0ngekslG2LjUGCCqTqfU65J4zAUSZ\nmdrunksTGmi+z2FFdofWvNXUINJMIklwI8bR04orCriJjvK6zvtJ+wIsu2MI\nDrYVoWMT2tuKszXV9wCxrzCDzqdk4+alSS4eweU9YvfoVTuEInQfRe66tcMM\nhra2JXmKt0xmROmlUSxxJNsAzcxTxB+dRNTFzqsr+nAse0jO3C3xfZDd7sy3\ni2pU0IXMhhg+/EDB0POhiXb7HEEzFJiYLeImr5H/fjezcTG3fhrnei8nJXAI\nqlrb5WpYeCN1rZKEWqQQfV++GKvoNLG9FWLvOCAzUBRInt3/yy96UKtrLZaA\n35fB\r\n=A2x4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCENHXkJYcUk1bJkYtNw2X4nfNMlhRwlchKSXaSNrKJKAIgOXetveJmK8w+FpGYG+C5La3ly2tGiJFuvRE9tGNy5UA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.2--canary.121.182f8dc43e81e6093c735822033f09354062dfb1.0_1621232068072_0.4095628159755198"},"_hasShrinkwrap":false},"2.14.2--canary.121.fa69777f38ac8fcda22426f80e1ee691cbc22732.0":{"name":"@sberdevices/assistant-client","version":"2.14.2--canary.121.fa69777f38ac8fcda22426f80e1ee691cbc22732.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"fa69777f38ac8fcda22426f80e1ee691cbc22732","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.2--canary.121.fa69777f38ac8fcda22426f80e1ee691cbc22732.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-NXWweYbmiZdes1Y/KklsDkRsrxDjFaPqMGuaqB/gCmFQ6E5q3VXlIRjrwQdipShf9XuI3IsYYbbYM7BaFo6+/A==","shasum":"c06e505dbcc60c820e26ec09eac9cada9dcdf8cb","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.2--canary.121.fa69777f38ac8fcda22426f80e1ee691cbc22732.0.tgz","fileCount":56,"unpackedSize":945011,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgoj9mCRA9TVsSAnZWagAA9UQP/2fKexCtJMn/c4r/IvXk\nPUpn0gLcbmgK1KgvpgNLUymMrVGQ5XWdSxwu+drFY6hkdIZcqDsyXeZ0kZVn\nq55yXLnZYw3ZvLmDVQrba+Imtu75vZBurXzE13gs/VNzrWRAfH//G8a30O5P\njlkcezy9vlUqyM6AGgRPH4YgWP31CV9CKvBU7LRMya/pjqC4WxNftPkKnlPC\nFhqQlV8MjkZhwat7N5mZU9zOEK5j+Ear67xzUMW0ebfCHriS1KLneyBOOBtn\n5ql2YBj9VUgKc63ybDTPlZJTk0npSaKKK/GvqYRDBGSXC2zvWd6F1xdpLNio\nBT8bp5qKQ4OTMyzZJtB9FnJICino9g0ZFYQfoQwvt9azSL0E/VfuGz/nwQ3K\nut4aTxAAJUVWf/craga8C5pfMaD/n/G/rCHyk1n4uV+/ZEiDr6yLrlkc4xBg\nEfhji35Z8HFctZFhDVuWUMfnQX5H7MlafJjjExA6C5WHNoMVYxQA2sdbz2Xp\nqiWsltSCiKRS7RDvGvr1v7hBgF+g3gJPWZnMIL7XHBywWfOFyDw+iytyN4bT\naShk9v1cpNkuiYcGzPeJ0nbWjxYykIIHRwhgFTt53wG8/ZzQ1mvFbrnB6WLq\nKSEUiKE6vGwAV48NCx2AFWlUk41mkf5+Tr7dYuw140WNR9ylmMXB+TnlCMd8\nYcEg\r\n=iZZq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC9IOcmoCpx0rRuk7+ZFs6Yq9qD6itlr4LkMu7tRgEe3gIgE1GLtDrxGDuylmYZLEXTXRCPAS4uM7WVPPQZ/1yX2c8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.2--canary.121.fa69777f38ac8fcda22426f80e1ee691cbc22732.0_1621245798293_0.9026768628354029"},"_hasShrinkwrap":false},"2.14.2":{"name":"@sberdevices/assistant-client","version":"2.14.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"99b07d1b48c20ab2ae4ca54b5dcb0614cecaa138","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.14.2","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-NjKW6qo9tFmfG884wRT1+qgUC51gWWUm4NKSWvrECLfqyFnqnL9FfZpCM142Wf9Rod6TXM1fk62YrD6ecQRCww==","shasum":"055bfe9df338c507eaf8596fa01e1b6897f9679e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.14.2.tgz","fileCount":56,"unpackedSize":945131,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgok2HCRA9TVsSAnZWagAATZIP/3N+BTVQplF9MFJxQpbM\nr4TLNTTN8pA07f+r7zPx+h97M966F9ekzeiwmFGguQg48JrGxUgdYFFaKJcS\nki2+buNLcXcMMTvU4pjxbH2O4g8k7PiSWYIDtTNRlJZKA1dovHEEY5kyag3f\n9Lb5PkTwcTlfQzUs53eLp/NH5IHr2Zrk08y1h+BTDvq0xDuPw+RDkXT+rYcw\nhXF8FFl/OVYLGqTYkjye6CApgP0bZxKAhH4fzDsx1HlQ+O5u0VLcP9+U6oJf\nZ/Ejx7s9VNZN9yIcDUO9llNbzfOFcB2Fdy+PKrNkxhus37A1Z8U25sdD0yms\nJDHiXlyip5C+/2Tx8ROb7ivy1PP5jL3jbqI977KRnZP6XecoM/hNZsUeTODZ\nS8ttp8M/PsxWJFHb+Lxcjrq5ADY4R2A+uG+diqxJH479C3qQJ+bD1w1lIxxa\nYLATjYaOFRgnBMPVnYNl5JJjq9Jwtju5n57KHCRZCAJIRZyHqEmsdd+19j45\n6Ve2700B/ji9vUswSW9NLmuwTEwBzeN3MAHVh6c5W0yyZfYB/XCE7V4m1ApI\n0G+t7JRqJ+ynpkbX1YI1rWdUTxZzqM21RzjUIAss4UoRhcHRzuKRxtnQBZgj\niwd7Yxu1zr8FuOV+bfF9eV+IrnSoqwiCYh4CDF7GM9aPDsv9ORcHn/vbDzqm\nHQ75\r\n=Ubk+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBibt/0c4IGWLhykkdIlMJmj5CG2a+dYduyJFcsIdT6LAiEAoNGavjA9qHdQNbZ7suRPdbgo1+XnwJyBg2kEzjtyMJM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.14.2_1621249414378_0.536093742499359"},"_hasShrinkwrap":false},"2.15.0--canary.119.a66f5caa41b24cf8a1b3f861c7bc39a35c1fd6cc.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.a66f5caa41b24cf8a1b3f861c7bc39a35c1fd6cc.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a66f5caa41b24cf8a1b3f861c7bc39a35c1fd6cc","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.a66f5caa41b24cf8a1b3f861c7bc39a35c1fd6cc.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-kiIeYjtOO2nWwVb2OPXyqSZmdgDBmCyxZffg0C9hVSD0OrGHFroDVLNVz+uLY/RcIH7BxCyxcMl4yPni6r7JfA==","shasum":"a8f3afde5f7362c9f84ea26cfcc29ff70b6428ab","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.a66f5caa41b24cf8a1b3f861c7bc39a35c1fd6cc.0.tgz","fileCount":56,"unpackedSize":950717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgolAXCRA9TVsSAnZWagAAwLAP/3vQ6oo4VCbGiAOLddzs\notM2WCPfTOSO/733kDCv57TKVHiAWTu00uHtkLbhzhCNfOAd+P25YN/soKFc\nLmYgYInriufIUPuEn2NtWKbFC67uvB4XHje54cYbF3QVIjv35qOyekAMqzw0\nW9SHkTHSU8ehfy2rYuWYqT8f2C7a2jh8PntOoYK8mVy0XzAs7ZG0gB7V7ijC\n/ODH56SFgFa2yB5Ig5P07OPJDuwu2NabK7AzyqYfZ4TBblIJPWuSOVgVJ9a8\nx6B1BFf61SQqlDqnT/sPXbt0VZGY6zZA64W0L4lQVMxpecQczq1TRPMaJJSC\n9R4YTUW9kJmPb9bVb8xn9KI0q7M62t3jV2scY9QZsGi3jDJCLuDSFg0f8eR8\nve/Fh4t374mqQ9pp3UCD0RYcM/Qha28dnuUFOVTxiAjOGxJExC0OwVXWmfDz\nog9bxdTM9Y4jzGAc+Uwrt3w87Z6923NAw3cL66YMbKL2sjsnIA5HaN8L8SdN\na69/PZvKUT1xckFXG5Zus23wZo8+FcE5jFV/6z5zPhw0pogH2fnwEaAidNfd\nQnwck02hMviwJyZuGtSzJyUHYg3wDYgi6E5Ut+YspPO4euYYvLi1u44ckqJn\nVJc53vkOfT0idpZ25PzYBqPqL/Emijjz62pVIsbXYuhVHZS6sYG32Ct4Fur4\nkVef\r\n=8MNx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFekZ7FQbKyHYR5tsXa78IFlvz2e4fvVXhR4YUdVFR/bAiEAhyILTNJmHEj05Ko4OAjdDfu0mxWfyBpbA5MWPh/UR8A="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.a66f5caa41b24cf8a1b3f861c7bc39a35c1fd6cc.0_1621250070674_0.05561311278044778"},"_hasShrinkwrap":false},"2.15.0--canary.119.546d344efee549489577d69798f1b8c0540dfe52.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.546d344efee549489577d69798f1b8c0540dfe52.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"546d344efee549489577d69798f1b8c0540dfe52","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.546d344efee549489577d69798f1b8c0540dfe52.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-24o7dV5qbz/1bbV9TVmVMFq0rPT7y69TE0nDFjFm7t1XNry/Q+r2FIMIgaoXEQGrFc9rPT01zI/RxofzP3RmOA==","shasum":"38b8ad4990efed7b352e502af2477204c5f163b0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.546d344efee549489577d69798f1b8c0540dfe52.0.tgz","fileCount":56,"unpackedSize":950717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgolAlCRA9TVsSAnZWagAAtZEQAJk2BOWsvpJ7vHo9u6vh\nnx4lFtC1D2dvRJS2ToGNOR9Zl3IpWoOuPuIPgijX4QpCSCXkbkSDLgbAaUjS\nR+G8QOetWFerhHb6MIHzeLKi7DBc+r238dQIUhT+SxYwFz0SUcMV224CIblj\nosi+c3TpuVymcVSiohCjqxISluRO7EuMKSMvZPGA0gUlZLiUXUxQwbEYHAKp\niw+OULDSo2sWFHkdop/n/df9TmhaR6kJghHsha3w0uyxX41GWEYRwbZ4FwbE\nv7Et8v783dM8y4fC4NfeO5nPU0yjePL6R+I3d0EJVNXAZcfm9ZDLYAeEI7II\nn3TaNP4BTvjsXivNYSTJl+7qRNWUNXe4jd9OXVH48C/9asdI5EQJhfO2xdcm\n3wMVY8OBRL6vo0kEjWhlRVRuj3rIrlBn4oqEET7IJ/ekTsN0sKIWkOgqQgzR\ny8oSrEbphJPSPMq5nNKQGA+AHU5TyK54e++Go7cpenUBuD3xKD4EtlA+XfwS\nj63cloD9UobkHsEj55h0Fy1FMiXEeN4G/HSluPgluF79ZezOMOCN0rj9gfvx\nv/ivCgDMAnGTRG5NZA+d+MdAUtcCj/arx97yW24rUn+9SERSwQ384ir66gHd\nnwXcZfGvGZH4H+7hTPR5BssKlWtoEPZrJYXwFLsRtkFAX0SZc9rLHA+B3JBW\nfyu/\r\n=t+iD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCw7tjk+KfSPwg5T7KKNYT+G9oZ/ChXAuQXja9WmNVM2AIga1vkKXoIyWUFYnlm7WWPwdXdSnRG3mIlijQ2QtLksU4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.546d344efee549489577d69798f1b8c0540dfe52.0_1621250084866_0.16272156019675155"},"_hasShrinkwrap":false},"2.15.0--canary.119.c6dca236b41a73f6575248452015bbcf247b0a7c.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.c6dca236b41a73f6575248452015bbcf247b0a7c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c6dca236b41a73f6575248452015bbcf247b0a7c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.c6dca236b41a73f6575248452015bbcf247b0a7c.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-F7vz6rfr8CPGlpDvV4XnNQatsYzGgCJ1uCnk5A47axtDmhtSipK7OMZNk3JPPUjU0y4Lvcqp2YpwWTCZHYH6BQ==","shasum":"a890a6bf5c14f27c44c6fd79ee137024933546f6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.c6dca236b41a73f6575248452015bbcf247b0a7c.0.tgz","fileCount":56,"unpackedSize":950844,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgonYyCRA9TVsSAnZWagAAGlsQAIH4jKsySIlXSyyhX//3\nS6jEPDGFDTlVviKKH8/fgGXlI6yNf48icb2ORhJBVBg+n/EhMSagEQuynB3X\nvYbhHXdDkXc7925xgIPGWMYb+pCsM+mOhgI3gse6ITTalwKSxXsnuqiKAhkn\nWEKPi6X28WH9haobeqmD6ZHSqmqXrwspZFK9yFcBw6r2jp/26FkTASeOzp3q\nYlUytdH0ug66S6RABh8oH4JdPZ9t52Id6cy/jQFjxyWBJA6k0AivFEiVN1/T\nwsKDBG07LAlSsTu8SQp6Hh5an8X8hTVtSKyt0B/bQA65nKzphym4kXCYO+16\nm+G0V8NTUJ8F+o/JizAuvYw76Ea5kmjfwbHEugjzz6GcocLQs7EB7F+jiNnn\nr4+IE4s3+HLzvW0AsMqAJFv0q/YWnvwQ1TmodKwukDsBqojOJP3aZO71jp+r\n1+Z/EOGoD09axLWl9/C4pS94doujfg4guQvPAq6NgIO24PxKtdvyFX/+n5Bd\nxJ8wDTUuH/QnnQHuT2SnAPkr/+D5vbDGG+mfRabCAo4HMX/lfC+M+pBNE9/Q\nhFjIm7mQZreonGWZc7iCFiWDi0qmljT6FSD3WWooB1RPO8BNJRQ6RkEG7ygY\nvjdHkho+Q6XpaKRMLdgxaC9ZkU8IMF0hAlzFsk4Ty50a+0ZA2MjhjWtwbCul\nDz3G\r\n=DbL7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF8TaQMKwDVknieuPCcGVjUJP6dNypiD47mTxiZ9Ac0WAiAZITzCFZ3do4dDEiC6+tuc/rZqkx4aUlTG7TyOpBGlOw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.c6dca236b41a73f6575248452015bbcf247b0a7c.0_1621259826457_0.43316119416462384"},"_hasShrinkwrap":false},"2.15.0--canary.119.c41fd4b4e6f2be1cdd529aa6192f5787c5b46cd7.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.c41fd4b4e6f2be1cdd529aa6192f5787c5b46cd7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c41fd4b4e6f2be1cdd529aa6192f5787c5b46cd7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.c41fd4b4e6f2be1cdd529aa6192f5787c5b46cd7.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-J6/6gpjn4d952sQXsd9nR4GAxZOYrePMWCSDbyqrJqvFTFV6wbjTFVs+1KsnveAF7ar/zOutRoztK+U6bOHlZA==","shasum":"611f77743a3cd31483984763cb4fc54d68c08dd0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.c41fd4b4e6f2be1cdd529aa6192f5787c5b46cd7.0.tgz","fileCount":56,"unpackedSize":950991,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgo20/CRA9TVsSAnZWagAA9DAQAIg1Tyffeeyqt0rLt8mN\n1XM9lYYAtHpY4w2KDDuv8a822+kbf8xXygbFU36n1GbQFRWY+TK+x1Nc4VrD\n5uj4FSBES52VRdY1mP0yKnuG/j7UwutGUUx/tdlQMtbDugfE7eseKu0ydm2V\nTtfY1LNuzGxGifB+Sj2ZL8DYnZNE37X2FPomz6TIorpjVIx7bjmqrk/d9tgH\nbrejxRtLQfhK6YGP2z9F8vpUCVTu6uHLrv9ondo1lIwjXZJT8lrvZkXuTgzz\nyb/ubcQ4WbvAaMPU6oVpj/1c0burmloUmhcSLOMTX95T/YRU7hgnUVEr60Lf\nRNkaJx1bv5LDq5FtW4I01GLy/OizhICKs7BDiuPcOgPcgYz2HTs+qAn5bEfA\n1RFKM9MKmqSihaMbQbWXQ9FtoTIS7oR52j10XdXsoSN0wT7qg0IB19Z0ndiO\nQpjkdyFSN+WPa7HEjSLTO/bpgnO0bu9Y1KFi9jcnuyQIUNEW5GJzY8ZaMyCZ\nYvaQEUJJ6FqZ2e1cBAkc2fetUJ746Vms9hWqqrwtnkTQ/I0OslZsiPyL6X8m\nh292qcJqPiTJDM+/WcmvoXeTf3rPW6iCL9jl45E3quTFnXCx6t5b6VI0SIS4\nfSACZcc964agZ/w0zieE1JogQqe26N8L646FHcrFAsSiunNFaE0faEXpeq3g\nyuBs\r\n=8Icv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCqtZwSsscukw4yI6jaH9rId5KkpkpvuhIyFR2ve2kZHwIhAJLXsGhn8fyW+AgzqVNZfQogGeVYnX52fp3dsxEwtTSb"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.c41fd4b4e6f2be1cdd529aa6192f5787c5b46cd7.0_1621323070751_0.2929255374755986"},"_hasShrinkwrap":false},"2.15.0--canary.119.6605900372ffba0d0a23aaf4ed0ec20327c4ea4e.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.6605900372ffba0d0a23aaf4ed0ec20327c4ea4e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6605900372ffba0d0a23aaf4ed0ec20327c4ea4e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.6605900372ffba0d0a23aaf4ed0ec20327c4ea4e.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-e+Z/EizJUXQ5Fcav6S94RD9/xS8YenRT9h3eLpmtwGbgtf3GwB4UC8D3Xeo8etNhjQpwF7HcVJC0OeoZwQUhig==","shasum":"31b0e3ef966fe90a675bff14d3a3f2af25a2f8f7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.6605900372ffba0d0a23aaf4ed0ec20327c4ea4e.0.tgz","fileCount":56,"unpackedSize":950991,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgo4VICRA9TVsSAnZWagAAtnIP/0b6DZANyB66bm+TjtQB\nNeKOE87d0QF/r1aLc8jF0L+GPGIs5uwFt7BBcdrqkQt863LncoLM5LWjcuu7\ncnMCHT064Nt1GzmgynByP2RvA1gRbV88VE4RW/JB5ipyLO4/i1Ddp6xSr37B\n9m08gzS5ohX+44MjYdJb0SM/FB4Q8w21GxraaKzv8paq7aYAPxW7HaUG1DCu\nFqireraAUcdtI5UWPa0xhOmWyBcpCruEwsi9Pw8MylW+MP42THFCg/IvLppF\nEMsEP0xGbhGmTFpqulA7wcM4oAXECHu5psnoyr+llyCRKuKWzHESD16DiHKL\n5B+Us/xNtZiKoG6An1EqrQVsHt+4vNbBulFY4itc7vVAMnOX1qK5KGs2Z8TU\nLGIFBbbW6gqyS0HgkyxWyMJh0LzBWmi0+I8YMyfidGmyyYK44gCInnJKnaqA\nJg2p/RHzevl44tVac0+d7DFO7tKWxVzanDNDwVnxVaqWVjdr7B/C97hqP954\nlFuNAUR90qQQmP/7TWna0YQrRUo85lKN/aviVMiX+WGHip6OWCLTCakoEIRI\nCUdxTh+8cHwaJEuSTmhIFRCN8fIDDzEicbumXXtM/aM9IoNs7n7RH5tYw3wU\nmZvhmFnhWN5a6xXpLG7BhTHa4Y4Oma17N30Xm6onETyDBiDKzzxkYlwdP0Y6\ngdXf\r\n=F6J+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGC3pdXbTtZklF9sfwWlsaoJsAM/pPFBLLwqmGaxDPXlAiA/0qnjbCAAX00U/IHIPN+SGm+gsCnbg7On9ozgpsMMzQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.6605900372ffba0d0a23aaf4ed0ec20327c4ea4e.0_1621329223584_0.7500064643473867"},"_hasShrinkwrap":false},"2.15.0--canary.119.9b9690335c22948c1d5414042d892177942ce4bd.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.9b9690335c22948c1d5414042d892177942ce4bd.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9b9690335c22948c1d5414042d892177942ce4bd","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.9b9690335c22948c1d5414042d892177942ce4bd.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Mn7qflRZ97H3feeeDDGReBm8KJPfCEnDJHzE3FEH45LbkzaVhzaUKaFqa9mScB1BL4feJ4dKCH3zlIu46iNijg==","shasum":"2e2f7a3e69645085d3e61947fd2a3f62ca079b17","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.9b9690335c22948c1d5414042d892177942ce4bd.0.tgz","fileCount":56,"unpackedSize":951048,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgo4WLCRA9TVsSAnZWagAAh1oP/ioBroP7/qYxqdvDVDt7\ntWCSvJBmxyyYnZxkohE/n3A13Ro/7cunR46SSD+A1KaNEk3W1xSjFPmrXRrF\ngiqP8ea4YjZWs3FbXje+a1eBLK5h9Y5H1QK0ciKL/muVIpTL7yXM4Jif3SVR\nwbKZrfLoiFqCjlL7rMEArXnZVKYbxLsXUQN1a4/dSgMmptMknyOAjxriKL7q\nkFxwEnZDpIEJl1iHdMpwepEC4s3GzUECwf73jH1xxhlJexOCpJydGof7TlZN\nqG5XhGT7TBy10RmeItczCB1s66XLMJM+lhN9w5kg8408dRW7FPaGXhHpzUUa\n/iq/Qey4bGl9rPeWPrI/zZXJst4aXOOevWc8drqoJVqqk+r24OwIWhmBUUpU\ndD7SI4eq5a27egN/hWzaXZBQWFlpmK4CA4zfCie2W03RUfRUgG09pqtyejWE\ncFj78YIxCCoA7F1dwDbJtyn0xyFHQlde4GTydCsT7Bg7WWkgAr6Ey8oRwk6Z\nxPlqTaJmjNPJind1EPL7ZPf37KFtx1MoPb+6P8xuIgF7cLGXEp4jlavkixDT\n5zqNRkqWXUgt90talBBAas5AHFrYEdYdO6gPJwfl7Gyi7MBfWa7ADMu8BYYv\n3KvS4aWqcoEqI+U+6dbrrNvAu9khRf8BU2prQXcykdwleb6iPc/IJt2lDJqF\njNjV\r\n=OKzu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAhzHey1V9Rf1B4NAOGMhRjOE7oYerTmiwSQaRlsxXhyAiEAmRNvVisgYAs8/4ZiY7mlGjB51X9DG2VItICEylSyIEg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.9b9690335c22948c1d5414042d892177942ce4bd.0_1621329291200_0.8271685559408921"},"_hasShrinkwrap":false},"2.15.0--canary.119.a094b4ab5c092ce1c55915d1512bf14a1d7edbe3.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.a094b4ab5c092ce1c55915d1512bf14a1d7edbe3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a094b4ab5c092ce1c55915d1512bf14a1d7edbe3","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.a094b4ab5c092ce1c55915d1512bf14a1d7edbe3.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-5ok04BjHbNgAxnj0HRN0GM3U/k6C74ef8o6R0S7Vy99T2mfjWkRd8t4iz+ldpNyZJTHTn7tp3iLh0Ik+Ywvzfw==","shasum":"7d6f05d77bd05f79795ca3b65a5082685c0a73cf","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.a094b4ab5c092ce1c55915d1512bf14a1d7edbe3.0.tgz","fileCount":56,"unpackedSize":951106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgo5rUCRA9TVsSAnZWagAAv7UQAJXJamP+6W81aSu/dsdB\nGE5Ypfo7l8juDyDRbZiB4+v4mE0Rhu8anqfCSVs0ebb6GYkKRU5I94j2xkXO\nfLZ7k6ZFDAifvJFYAUja3p2yE369EbeJ0k+K7XYezAbtskDWI2/hT6k0bC2x\nq6Duov9ki2USH0uwuu8fYaGQ8UMhUV5kn3OacuaBhUrPAwJ9Ah+bpujTSG3t\nMyfZ0z0K4CCRHWHwSiXU5/dpwpiE/GILkgGD5qHtlRtrRhXlRSKuz28B8zrp\nei/mv6f0V/mw5YRk4UI0aNKJjADPKpTiexQHmLIvq6gflXkmtsQ/PidV/srb\nEvMzMtLnCQ5s5qHtt4Nm8Ze2FBAFtGDvlpd2Zx2psVvSFl5VEU9zsCl1jaHr\n1s0EZALwxd865VB78NIfeiIwjHCDy2WLZX/AYdCkdsxkSHr6nqI3165CA8iq\nPutidOCo5DeFi/lyBzGkOvMPL1LlBXc4f6LW9uKCQH5xto+UGVYcP8KqLuMd\nPCs+GMAf2mr4ekayK6x0qzmPpuSH8fF3h3lDq6dHpmhEwfnrKkGE/69oP1yq\n2MdxCoo/zc8bGdiagOpTkjbP80mV5A6Le31B0SoD1mS3HQa1+GgwmzNC6vau\nqA0CwIgdTrHTMgYWqaqczHlEB72qZShL2ZH5ii4RRiI+8U4eBkpw2i4aZcLI\nYryt\r\n=fl55\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2Ppe6KxEqxwrmrOUANCeC5iRPbG0dRyP6eE9JT65tygIgaRpD3CsCcwGwDP4tioDXXHfsG9vhSh2p/odAFIAFkAE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.a094b4ab5c092ce1c55915d1512bf14a1d7edbe3.0_1621334739587_0.4823450386907464"},"_hasShrinkwrap":false},"2.15.0--canary.119.28ed78d731db598da92db11ef37c49a7f418c8cc.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.28ed78d731db598da92db11ef37c49a7f418c8cc.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"28ed78d731db598da92db11ef37c49a7f418c8cc","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.28ed78d731db598da92db11ef37c49a7f418c8cc.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-dLgagLS1J+7bM7PLIqjLoUSc3t3pAxs8kSR2uUEYuOCkpa+vJl+0nRE7eIp8AEKvkrg7zTx2KWV9mBP4HD+/Ww==","shasum":"f915c624c0da71017a555304eb776120e2e6751e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.28ed78d731db598da92db11ef37c49a7f418c8cc.0.tgz","fileCount":56,"unpackedSize":950728,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpNvuCRA9TVsSAnZWagAANlkP/2B2bQmOV5qbi06lCup4\nu7BJn6MZUtR/dS5I4/N5WGS0M84I+ejOZeRoffqGhgBcr7NRP8yYQoFdY08R\npwpA1yJiSz5I5kxnMUMJuHqZ0lLL5SBVqYp5RCdAgJlorywu2slvvmSkI33t\nPE6lcAFEGNuPzPpY/AGsqdd93KUb2PZ4/1derZl3Ez8IrmuvtHVa/ElBmecf\nYmOx4wEISFPU/VzDbP2XfyirG+Mb2RJWkZDZH7QNKlBraI67+atD8WBSLVCy\nrWb+Cjy6Ztgnhg/5vsmEDw/TAZfNOTiitCZi/YGcJeXcvbK3mIlnQ2vQBld7\nodY02/KDczq+UbAOx6gUNbRv0eBjgQ7B2PoqhtmvvpfbhwIpUZthH3mki3wW\n5Qn1wOjR1APyIn3aTaPLYWN7+fHOqtqDx4MDjJWqHUTMPzGxdSOzx7SD5gH3\nPw4pjKQ9yaXXc/G05PRuu5Xq2NyMu6c/13WQLrFklC1Qh12e7fV+JNJxmDEv\n64WAiNVPgicJrWXxZJkLS1ql3ffJQ13y9Cl+sVt+y/F7T9KNXz0G7xUN2JY/\nxCf2QRy7mCR6Hdf09OGiKkc1o158utybauivENwJmS9CZ/htQNNLv4nZxRFn\ngRPUq5E74JAiEKA9KfFBUbG+6u3zqKoLMtXTQu37+xcoV2znUaL7Iz6p0bwz\nJ4S4\r\n=2cPS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTlhQI91knP7UqjIm85qelZq/Fm1mZaxhthh9q6jYGBAIgLqhk/s2l7g9TpYg9TUjJAezCnU3VcN+hPiqUUQ62qPs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.28ed78d731db598da92db11ef37c49a7f418c8cc.0_1621416941975_0.9692551355008097"},"_hasShrinkwrap":false},"2.15.0--canary.119.1c4c2e35f4861700dc383473bdc5eb928a8da427.0":{"name":"@sberdevices/assistant-client","version":"2.15.0--canary.119.1c4c2e35f4861700dc383473bdc5eb928a8da427.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1c4c2e35f4861700dc383473bdc5eb928a8da427","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0--canary.119.1c4c2e35f4861700dc383473bdc5eb928a8da427.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-d11wgP6BBXQDIbP8gy0ZoitehpqyGFHRdQbOZO1txe6RJURn7WvzRau9P/l/BS2OOftJ4L2dIJGAeJuZVn+PVA==","shasum":"2716f4f5145af1eca054e8a944f202e57fca016b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0--canary.119.1c4c2e35f4861700dc383473bdc5eb928a8da427.0.tgz","fileCount":56,"unpackedSize":950757,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpOPzCRA9TVsSAnZWagAAi30P/19D7lta9/oy0mA+Afec\n1WeKPK3pi0kybUx8rmaZDKc7ri+F7/iuTayRtbztUnd/9Z70qbD653ADqq7Y\ncYSf4SzQsFiVh7Exf94N+BommuGqU7I+o1z6FCiIyK2dB5gZjH9IicKp0SD6\njKrT3BL6VxGc2zpF0O/cl7F5kMlWUDOR6TAMTdSUk/JyGIsICnv36vJG7HuM\nGFa/gXjK+yzmyckb9Jl6FZJFgnVevta8wFk7UhcqX5TWflQwG6tdybXkI6gS\nD/w7eMkQDN6lmVNq52yzVOXpQy//UdY59X5HcJbLP7Muia0kbcTpLq3crruy\n4if3AN5AYRLSVFBkhJiWH8XB8Ss275GzhfrU5PTxiOhuiB6nhjPZYw2sCSzb\n62xAYhIRq3OrW+z6oz9+6C1tld9Ka+MTYRmIpbiDAcPvf7XLo0csaofwBjm9\nB9f+JXluH5DmnoNWbj+LupUnGM4GFJE+kUyHIEGUPjKACQjLwvsmIPYQ2TWb\n6WEj3F3CpOiuRFuj/85Qs1JB/pfEceOyMi/5c9xDIZv3mjs4L2OGoga12I3n\nu8ey8T2LK7N8VJzWHm6x7XAOGQt7+kxoi6vPyQfDdAMuSCig81zqGoYMOFCi\nGxyYvjJJodHSmSeisZTGnMcTSxGIDra9/BmaPI1Gyg+ggUC+9GXb8xSt0oGl\nZtt/\r\n=HxD7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEIkkNOVgid0v/jc3b5IGTJgho96Pgr6jW7LGeHeeer+AiBIsYc8F8JCBxqxEFg113T5A23KliCWoIgitU3gDJQo7w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0--canary.119.1c4c2e35f4861700dc383473bdc5eb928a8da427.0_1621418995154_0.5886314178599357"},"_hasShrinkwrap":false},"2.15.0":{"name":"@sberdevices/assistant-client","version":"2.15.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e4883734e177ddf100d1ab06e5f6747abf8caca9","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-p4UURpfpv9v0ePfjGgKOhny9ojatpCnPGMoIcGt+cBKO+6PnoU/x74Kmdb4oujSrq29HZqsIXJlxHyVTq4GxBA==","shasum":"2b27f1c907ad8a174606c00b45fa8fd37ea0d620","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.0.tgz","fileCount":56,"unpackedSize":950889,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpOtyCRA9TVsSAnZWagAAyMYP/2l+XZb3ChokLl7VWdJF\nvun6kaX8zLJhGB3R/EIpw0DwhWQ+Aq06oMx8eLCguSvUV7JFtO5tahCNajpc\n3+gmtmo4fc/yQK+PnvdTXnxAOgR/mlltit0OD3UzW3xVrmBfVIGmNzSxA8ak\nMTquZISA6RyYK+tx7ioP4gaDqKQGCeHqw4IY59jSGccccDSNdlQe38TJiTbA\n4/mLT7RtSEsNAxOM8MqKb8ilwFZDTt4V1l8yU/I9Pyo6qvugR4K52U+aO+20\nZq75/VS92N3frtqtsBTz2CuEbob03vmG9OPehZAGchJrQr+sr5Vkm7sKfTs/\nqipaQaF6neG7ak6fi6iTHUx/KZh3TZR2uo6OYK7oLurRgLVzIuWQhWpE0Ed1\nZqg/YhHaRTIoIUlT1yMtfVI7CB5Qv4th6ceZZGDPt07/QmEFeFqpHc99CjSp\nq7rN7naVnEva1zS+XGh47BW2srVDn/ml8lcRjzHYWUWVnW2lyKP9yvd+m6Xm\nlfrRiWDhXP3HeLbKL7q+VHmaEybe9HcFBajKSkdhLI3tjH7DgLulz0exDzqe\nhCDAPJjIyok302vcbZ/ObBOsSKtfXZh44YzHGis20UqxYjJxH99PrL50dX4S\n0xSffoSIyOKTeMyrPwpETpH8r1+L/oQeaiAb1Dd4S61pvwkQ26sQyl1CtR7G\nzI4j\r\n=+1OU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC5FDAL98iHL8PFG+Yp3nesr4t7VPXSyjlidygBvm2gSwIhAInDC63h0xSm7BeI4cVAJWR0osEE5GFKKO01i1Jd2CIR"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.0_1621420913862_0.400189141254778"},"_hasShrinkwrap":false},"2.15.1--canary.123.1cea42c1ef72e9d85cb7c81eadcc51809f8d7dd6.0":{"name":"@sberdevices/assistant-client","version":"2.15.1--canary.123.1cea42c1ef72e9d85cb7c81eadcc51809f8d7dd6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1cea42c1ef72e9d85cb7c81eadcc51809f8d7dd6","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.1--canary.123.1cea42c1ef72e9d85cb7c81eadcc51809f8d7dd6.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-7+Tisahv0f3XKnH68RxmSd+LfV8ohNG+y+oexQVFLs+f6RDMBBah9OcPhKrtFwcmOoTmqAuvbWe62r6yBpxs+g==","shasum":"788e1069992f7a681f7b6c82574be660db9bea2e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.1--canary.123.1cea42c1ef72e9d85cb7c81eadcc51809f8d7dd6.0.tgz","fileCount":56,"unpackedSize":959867,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpQt6CRA9TVsSAnZWagAAJbsP/iCaCYnklgNzoPJf17+g\nMaYLY/VbR5eAAyTowIA0nZYuE4S1eXuqxDUgqo1/Vu+CXWSH56MKHSLogIC3\nTrHLLp3i1TLNwA6RtmWUxAiMvZ7U7ky+Kive0Cg8YPFXbHj3Z+JXPXnMWevP\nfXB3nZwqJ73lDpoHFTdAv9npVlx252tk/QKUiJsF7xzJaxAL/Qf+JId6wCk1\ne6F3q9FdZlgYATcUjLsqDYqVisMnpwWUF9VS5egFkObkAkBoW/TpdRwXmEnm\nj3376L8ISQsesW8H9K+KQIMgqxdVycFCwcDTy30Le1dO9NOCyVO5vZdXsZvi\nYesVDgwbRaDwZxYRMmfNEO+9B2ECL+bzwUfqW1ia6rtzGdtWNYa2ulLQhXms\nyIDG+CGB4P1W3fffKFTVgLUxXOIQpfdZAl1paDf1U4OMdLrc/K50f4UiU00w\nCNUdt6wXYwDeSQgL/mV0munAQ4UUpfbF5crJFZwfDmAmIr432m037T6blXDi\nVDb46UqDMVejMYDACLy/a11MMZJH7r5oK5uUcW6OCdXJ4a8Cp1VIlMKNPE+5\nuTKE8bccobaot75wQSy9JTG9/wavrklm100a+eyZ5Ymo5dCgq9kG/94cEZUp\nwEXHSnOfIh0wzd0wy/fNbXt/lqgPzRcEnOu+Q6McdFQpc29JHRSvaCZhkqmZ\nB9Si\r\n=dulh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFZJ0zNgfnfFaeyvdaotJan7w7+mwzn0pjy5N/8u0MNTAiB2pNPftfj35BYZGVR7e2N8S2xFvCIYtg7+WotZXvQUtA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.1--canary.123.1cea42c1ef72e9d85cb7c81eadcc51809f8d7dd6.0_1621429114282_0.3649272020094039"},"_hasShrinkwrap":false},"2.15.1":{"name":"@sberdevices/assistant-client","version":"2.15.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c2f787f4c0468de9e1b152640a3c4b3b5d1925c2","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.1","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-yISxKRlylZKpfvF79ktCSb9v8/m4WRXradk+nFGiPgwfTGHcrMvVe4g4GMGx+isKM4FWufNA9lKSPTe6rZyogQ==","shasum":"5b4a04e37a212f642fddda71898d1947fdc71762","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.1.tgz","fileCount":56,"unpackedSize":960003,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgpRzHCRA9TVsSAnZWagAAJ1cP/3B9iNSoofR40hfkmQLQ\ny2gRcDb9JGT1wDuDAzxwR4OFQ66ydE3PrZcQHJsn32s+dTVVNmwaiBKkWHbH\nSyLdl7m2H9gox+tsuSLCrDWF7IUyx1rYchxJmKf0AQyQfk11/XsutaKZC6oo\n/eO5D37p+pqqFZlotR5fgSWJVIuKiKX8h6S5c+0uyH8VgHCdRBQU5sWe65UF\nD6anqWFPAPSjiEMxHS5zkEF4EnpEnnqz21+Wt2Z/gAspAL+arbouY02wyk0W\ngWiJAjn4tQUxCgEbRaAPXU8RJXXjSMQrLRwyhoz8mA9DUShyr8ctVG08WUMJ\npEIVgV4On+S1kX3gSIGw7WB193ijLD6r+vVtBcZxNhZULHDj/RIKgyBvUE8D\nBnT+XxtHxsXo+EZMAd1bV7VGBPHFgpeK1S442im7JDWumMPybqkHwYRqL79B\nVAwM4CQoadR9fA4eEchQhIZRESy4USv2ikN6CbHpsUQR0uoJ6w/fEaAiPOsq\nsiltCrm1JhWI25yz5b1N/DpTRHgfgmhb5chtTMQaUxfuis7ym+BKXGQeQkeR\nNTFClOl9fdVao4JuyaJycWpfEoctYlk7e9GbSsDxzVjHxOxof1xFmH9anSUc\nqBvR+HoQYbl0FYym62WIPUSBn89SB/leUn66yIXN/aag8Pg2NvctdaJd8p+o\nR9fp\r\n=Nb8A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIXzkZez4lMaZBE3kjWYcBxs/xbntXhigWBqMdx+M/XAIhAP5oHCjMlQ164Ado5WeLk/vggBHGXVDpYOhkwqmXJ3hb"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.1_1621433543108_0.44715338980382136"},"_hasShrinkwrap":false},"2.15.2--canary.124.4bd9bbfe8a19b608c7d7b77e0a91dbbe691d5b71.0":{"name":"@sberdevices/assistant-client","version":"2.15.2--canary.124.4bd9bbfe8a19b608c7d7b77e0a91dbbe691d5b71.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4bd9bbfe8a19b608c7d7b77e0a91dbbe691d5b71","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.2--canary.124.4bd9bbfe8a19b608c7d7b77e0a91dbbe691d5b71.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-64HRQi16/NNI063WfKEM6AHvEu0KvP9gr8pJtzqcYBC24PGpqa+p6MJk4HgTZZDFJiF3KI2q5M7oyJWcEmndYw==","shasum":"1d291400b944cc396854f014f5cfc4f819171f59","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.2--canary.124.4bd9bbfe8a19b608c7d7b77e0a91dbbe691d5b71.0.tgz","fileCount":56,"unpackedSize":960302,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgprCxCRA9TVsSAnZWagAAvTwP/RF9u3D3Ajipz7iiBxUn\nvrutvG2ib34mjxrwji5PcvJcxwk5ScFxwRA4kLX3GG97J/LSaCiS+cDNjH5o\nKKCcyyktQ331I2ItVgNA81megbKhYMjRudafT/yTIbTIZzH5fwJmkI8xyDSW\nyKNRBqO9sS5aiqO+eDDI7VuhMmndLFHW66iGGsYxN6/UgU3bb2WZNSNLOtY9\nB5zbaESGIfg6FKZi+sFMUt2xfyqmSpolsybjJmHEqoyfUP/x+spln+4t5uFX\nHjA9Q3wJ66yhfX04HeS5h3NfY2uQhbRZu0fFDbrVgPdsK3U0njwj1OIXxBjz\nY9n/X3obgih14+ozqMhBBspKEszL3yVdiwqNolJ+jAavU0HvvjbMo1uDA6xw\nfnYZFWUowgQBDl37PIXrFPI4D3NOS44tcupe/W7Qw792lT2wRw6XceoygEaN\nRLuMbAerrKyMEuexl7D509CxczQQJqCOQTTTD3cJufiXkglvJfXAUu5e8cVl\n9+QKCaDv8EyIyGUW/PDN3u0YFKQjXBGq+J5nIQp9JyB3EJ83qvLcVcOMCyog\nTNFc/LbgUnFJgaVKz8P39Bg3qk6Byiso73lnnteMbIavFFh5prDtLrZSZae2\n1/071J1718VCn8vWd1sWjhEM1JrOISXPkvHoOIKFFqfR7e6viG69EmFAyKye\npUoe\r\n=8Dwy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFue4Ug6eh7kDvZP9Uj7qBjrOPSWpOSl2ed2D9hgVSgBAiEA2oTsvmYOyBVAV9Gw+/ascYiQY10kI9h6okqvWqHJCZg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.2--canary.124.4bd9bbfe8a19b608c7d7b77e0a91dbbe691d5b71.0_1621536944788_0.2803100589487588"},"_hasShrinkwrap":false},"2.15.2":{"name":"@sberdevices/assistant-client","version":"2.15.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"28b9d97f25a6ffff428bf476ed46cb827252314c","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.2","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Gv0LPjd0rHM7cQxdVIN+x0WbNtYPJkMv9FtzS1VCgKmkxytm2qKdXUce3kLg1Bjg4xsYGHErUnxuTfDhyk0KTA==","shasum":"b8c2675faccf90b6007c4962450d194ef8de7421","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.2.tgz","fileCount":56,"unpackedSize":960436,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp0bHCRA9TVsSAnZWagAAjtsP+wThD5fsuiH2bcxl4Yrm\nbRMOdzEnXa7JiKANOj51aYIeibAjHrP5ryBRmSFnELtL/zustDpM/c3n9Xmr\nTgAlZUBafgGl09wCf3075JOxCVg4AQRGNdc6cOoFUM/NBe8MUpXGgBPJZUFh\n7WSxlwJVWpuOmWOt+IZdoYAUKg/SptneKDSAMqqlQVyXeL1LUm5FpTQoNArL\nwsR3Q5OaGUwyF6tdYi4w3Hx+9wftwxqvLXbybL0BYcxnCX2z6oZkL6CSRfT/\nypMxsRNymQsbe6SXO5m3/u5bG/zk0+q3ksH0jjBSLc6UwrJ7Le/MDE5L1eGO\n4yXB2uHm3/DqqKfpHDbm5V4IFEd4S3i1ZT091v11A1C6jNG5mYaEkD7gxnAG\n2X9SYL0ntzJo97+tjP720jaOGWslY/68iXGBdxCy6W2AasHUf/uX00riS9AB\nU2dAxE05SuXpkdtitISooZ2AS1QheKeAnORh4V1mmgtoyD4mlc307r2oAM6e\nh4V/pr0W668fi+hkKRGNJwaQDlYJL8wXhO8JiG2ordWdBhOOaYYa/GR+u0ks\n3742yptA8GOSY/p+dpn8WvkdJIbV3cHb7QLEj4BIn6jcKT4VYWQBrCny5WjP\nUpmfkWG1euLhs0ZpqFsYWBVerph2uApEPsyQNcRE2WDp64wC7v0vkn+fQ5bH\nldqk\r\n=+cU2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDmMUTZOkorVnALW7ztn7ldxE3aqwA49+6HUICUqAlPeAIgCPPDPrYmU2oFmu//ykBpZjpUIf9cgmwBpij1hB/6vdQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.2_1621575366435_0.3104156239315663"},"_hasShrinkwrap":false},"2.15.3--canary.125.940a4b59206aa732963527d300025d8006175549.0":{"name":"@sberdevices/assistant-client","version":"2.15.3--canary.125.940a4b59206aa732963527d300025d8006175549.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"940a4b59206aa732963527d300025d8006175549","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.3--canary.125.940a4b59206aa732963527d300025d8006175549.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-LJJFtd76zLcOrMmVjQKv5Leql6VQkb9ggZ4Vm1jkrkidwZVoeMIknIM3t6+M7qUqRh8WKK5+nXoHzZ18vdEnpQ==","shasum":"6ae24c6ef704bd4b69a76c93729562909f85329d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.3--canary.125.940a4b59206aa732963527d300025d8006175549.0.tgz","fileCount":56,"unpackedSize":962577,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp1afCRA9TVsSAnZWagAAW3MP+gLUjU5+ZNhoOgRA9PT5\nsGYdZdN0tMF5OLkkUA+PCUiKM1E6EUSrnI2ca02g+V7cGBDP/6Lr6Bn/bmGV\nIwdQn3O44dmj9P0KNt8pYD4DaIgOB4bPl4Q/WBsiemxAXlBDidG2FiijN6HC\n/LL9g4wVBKEyRHr58831/a5fHhG526QmbxLtX+PQSR8w+0YfbUoqsMg6BxrM\ntP3ovLjC5aK74aF+0XPHlBAMrZd897J09xihwMmCuhHFIu/YbVy4o7x6TmNv\nkY45HNSyysq42nCIHD2GXqjwo9q3DYdwxRyOkzmLCWiWleVIaem8LCT0IwXD\nE42zP5Ua12ZK6hr1Ke9WUVlNA6ihgRSbgC4/kaMRSt4vjNxZh34SxDvKoh4i\noE/LC7raiQWWIpALx+LxJ+BTxQl7Dx9gXt77kFHiWk0pPpkOsltEn6tjT1dL\nZiyjp0hCYCJxqqA5C+6wzR3PQVr10LwW/oqCL4LejRBxj7MHtPPQnrQ+c38p\nYEEAyNgbC9dtv8N8C8f4pAZps+tTTnrJXv00ZZU36Ef76hD5A3TTIpdXTQcE\nWG+6GrdWfr7qGSmYN02dEOnz228vmpBncLkFce3hmYcXxLj4Shuwwg8dIYGc\n9CJEFjUfP13GM0RzGH6QK+ekEM6sUMXtlaHHKNxjXX8Qkm77puqbnjyL8ccd\nUmfS\r\n=pRLC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDBA6mo0J+9GES/BREMrURiG0DOVLVmumVO8ohozKQXkAiAKFj5dlddyb/kj4i7RbLxgI3BKOIhq7VXZO01eEPFLRA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.3--canary.125.940a4b59206aa732963527d300025d8006175549.0_1621579423151_0.5377771964244176"},"_hasShrinkwrap":false},"2.15.3--canary.125.c65caa0902d6469fbce4acdbb732bd46e410e5a9.0":{"name":"@sberdevices/assistant-client","version":"2.15.3--canary.125.c65caa0902d6469fbce4acdbb732bd46e410e5a9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c65caa0902d6469fbce4acdbb732bd46e410e5a9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.3--canary.125.c65caa0902d6469fbce4acdbb732bd46e410e5a9.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-sWNsp1ProxccH8XKk0ZNp0MFxcNsHWmCZ0jo3VuyQGorWSy5gdbZwJGgR406YuzOU3W7B18vyC3j16wwCJbuqw==","shasum":"af666cf8af847ab241ab6ac115361064e836afc8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.3--canary.125.c65caa0902d6469fbce4acdbb732bd46e410e5a9.0.tgz","fileCount":56,"unpackedSize":962761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp1mmCRA9TVsSAnZWagAA2/8P/217vBAEioMJAi2+Rq7y\nY0voyeVizoRgj/4dFgt4BWN8lHtYL+Xl7nKlfvkV4GxlHZQ6ulmT4k4n1piy\n9tgJ5yl5831Ax+huKrKQNKi4Od4QgE7iVHL4le9eXeTwNmpzybaebRzPffV8\nVZImWahtEDjuMwrGJJVx04bbj87M2jivZfRK61LMOhZxTvJle1HrzGYYkoKu\na7FATzOp+RXGrU1rfoPBLrzG2R2zHP7Y7AMOAUDYl1RpZG4xvyaj4WtfFYSa\npQuNU2lu8GYW1eoM7cRcwh2YricOhcEUfMn/cH1SIn6StkKNAL5jU8XqcneT\n/65zzv8z5wlgeW8cHKqH4xUil7PSfs3Q9Bmvo38Lzw8r8EltPtlqna32rFqT\ncbZv8g3sYONTJYEYIXNAPYf0aKK9MAZ6zL0klg0iOowS0NCQOnAcjjKafS3/\noaXWMqnEmMnIzzXStD2TqyzMRHFXamZW9CGfSHoLCJ4Mfz0QatkmLQJciyFJ\nMYNP7YAczkrI4LiMebEy5ZmgFZoFPr90iQaDhoLMGDRnExOZ0b2zthO9HTgs\nrq9fHE+HuHgSblcL0hPYsGBJe4LYXiHsJW9aXFtaYcC9lZAVOItxrHyR166+\nN1GkGMx0Ram/yTjajFwKUgEb/IulXCwSqOsp0OmNdyumdGhvJXJoKflj4/qY\nFN1t\r\n=pc7n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFbTdM1PtPl5EPEy9989E+C+ap1AQ0ZYbhX+rsWbGJayAiBmHGfnLwhGpUMTDw0T6DVYeaVPSUS9g9Dn3bbrEEEi4Q=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.3--canary.125.c65caa0902d6469fbce4acdbb732bd46e410e5a9.0_1621580197765_0.07983428710990004"},"_hasShrinkwrap":false},"2.15.3":{"name":"@sberdevices/assistant-client","version":"2.15.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c08d485acc235ffa3aa81d9e5b400fbb90326698","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.3","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-L4Ml2IwLfLE+bpB2dQDMM8nAxqnRrhhoidyguzw0mbeB1Y4z6rqBTmxcn5M5FDjDZ6YKb5LXglRO6LRXFBxctA==","shasum":"e9edd738731ac93a23827f5eefb860a83345491b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.3.tgz","fileCount":56,"unpackedSize":962886,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp1xoCRA9TVsSAnZWagAAvGQP/1VCAah2/OvPcAmKFhbv\nIf0uCtWytEFkOC7Q7IQyTE0uQUYcEH0y8XaQ3GVfstW8na5kvpuYYS4PuQOf\n+j/+r7fkG38qKUkUsW4mswm/lyXqhffbN+RKYnBxk2LfT2I8uU7O9XDkykTu\nbVWm/QSrAgX6hiwOQohBf8ThaUY4vnml6hRFK5ArMM01EqnKakWkmBkG6ghp\nT0dv/QTiBSxEm8sX265rjW62MfSbXm0p01nNNBdPCydzansq3tL8ljW7LGoG\ndP1Jbtcb6fPuni6qQ72dgPEwi3h2Lrwqa0/rewFwnz2fyVtgyXGEQfHUyRMT\nU92lmPifA4o8zVGSSuD4CWZIG0iFBCWxb/ARn9U0qq7ZQeQPd8Ita16BkD2b\nxZHDaQ5aKLb06TQ3jl51Dl+HKREQruzyHbF5Iqt03fppOMovooRDs7IOHrGP\n1dWyUZWCcDL/vhj0OLoju1MZdRl9WWB5A+FjVRmaw/ujWGzCZEIqOziqzEsn\nu6+xCymu2vlFlbL9p7e2s7hNL2daBUSFdfrE/tKpii8IqRKPx97UPuqA6wzu\n++S8OtagOZnb7umW+fnoFqDH4NPEZ5QAOL0as3H8DlO0xzmnUTWgle+uCpkg\nCGMGT+PqJ8vFMXC3zl3R9K2J0aSC/tBeTTqgu/d0Bs9wnT+Ug9rPqM7zIvYW\nYJk+\r\n=sJCN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHCtbXk5nafRAvh5GD7o/SLuWc8kJVAx5qQVDhwOgfKgIgCx1jzrRtYFp+C2LCPPm8GQvwSNqMNX6AEdJnKmXu9EQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.3_1621580903391_0.3019178796252422"},"_hasShrinkwrap":false},"2.15.4--canary.126.fe34455232d85703ede3aaba9a7aabf9b6f72877.0":{"name":"@sberdevices/assistant-client","version":"2.15.4--canary.126.fe34455232d85703ede3aaba9a7aabf9b6f72877.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"fe34455232d85703ede3aaba9a7aabf9b6f72877","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.4--canary.126.fe34455232d85703ede3aaba9a7aabf9b6f72877.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-ZYvlF1MvCUkhteHWzgW6xMTVFebZ1SxOqFbMYIvkev5Wuh65hzJkgtbMaIeaQiUjidw2bz456PJtcNexSxpS2w==","shasum":"a09cf14334278a22cc6d8e23c4ef4120fb734ae1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.4--canary.126.fe34455232d85703ede3aaba9a7aabf9b6f72877.0.tgz","fileCount":56,"unpackedSize":962110,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp9mMCRA9TVsSAnZWagAA5HQQAJxKBhky71hRxNuZiG1Y\nuVpzMNTZrA8IspzfegFlBsin8PrScf1IqBWPTEa+4q5KzD5r6iVurFP8nGAp\nMf/IdsFXp75lvXN+LhK0TMDUunSX7FCBi/GKBYFqdGkoge5mRZQE41Y8SAMx\neUy5c0uj1q82mASKqSDLYIdOE95+yBmOi2OZh1Cv9fqpXv8huR0h2dYH2aVr\nwiOpfMWDCNw1JjM6CgWVMUrWaeP5j/e3IzMOwEZ5qTIAd3jY7X0fdfNdD5Ja\nqXMdJvAQnPWHe5codOEALe6fFCmeS9gSTZhsRRZsmCyQ9PrN7iPMKMNcDG9d\n/1YU33yC3tfO9p6EpcmP6M0EWv+aZUt7tHlXS/V25zkOQIOglFS5marp9A3/\nSkmYxo6R4QPUiuoUOkcWZtCpcOb+GoGptcSMlpJSo/ULPGracjseWY50+s8u\nMIL34EgTTgutmacUWg35078PWTRukL65rmW10qY80MzvRK3X/N/6y1E/Nw00\nZBlroK5qq/93JTgHo1dkeYmo0zqOBbmYw8MGZymOpoRaFcikWE4ml1kVVJfr\n5oiTRdkKGCV1VBQiIRl/0LeVmVwdrojAtAi79mOVzdiTPCGIFZms1eaZXKRF\nNvfJo8UnbfXL+khLpSaEWi0iIsHfJ95hT7B/9AxpIInzGPvKm6Vp6aNzMckQ\nevqb\r\n=3RYo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCGXHa/Sqq3ODt2EJuKt8ZvZZCBbmfynbK1xng/ipuOtQIgV3CA6CUCj90L0ThWsJGJMXmerLGaT9sdbO/mNYyH8xQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.4--canary.126.fe34455232d85703ede3aaba9a7aabf9b6f72877.0_1621612940143_0.8116163371205645"},"_hasShrinkwrap":false},"2.15.4--canary.126.37f78c828f378be77f9af3adae78c9d0c888d67f.0":{"name":"@sberdevices/assistant-client","version":"2.15.4--canary.126.37f78c828f378be77f9af3adae78c9d0c888d67f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"37f78c828f378be77f9af3adae78c9d0c888d67f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.4--canary.126.37f78c828f378be77f9af3adae78c9d0c888d67f.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-9r0TjT8gmAE/yfODJILtnRWSgy0e4cm2N+8bCIC/+IqiUOUIb0rEt4kPLPLVLBYLH4k1wZfhGPZX2zQJb2Co2w==","shasum":"dee4f88647ac565e216c748943cb6eff3232e6c7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.4--canary.126.37f78c828f378be77f9af3adae78c9d0c888d67f.0.tgz","fileCount":56,"unpackedSize":962138,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq01ACRA9TVsSAnZWagAADMcQAI6120wRzi7ARdkA2txB\nhh0TEQVbuAyw+OjoFgMBoAAvXz62A3+B9ZrF6n7i0hgYVznyAQYLfFjf5DZ7\n9PsAVEX6cqqfhEFAoquWtxVaH3fgzvM6YoXrD6cZwkjzxZxTVRXoIYH/Ymg+\n4eERc3b1Z5tZ70lrGoE8Kb620ILpCHdGM4tVEpVq8BNiCO4FsQUffuKWkU8v\nEz10Mg0CCh4SmH5h5OZIf0uAEwwArOSQwCxK9XSLuzzQe/cQltzWMD4n4ghF\n0e9h2FARFFhjnKpoU1GMtHoUx/MU+LuBng4VqZN5fP6wNoOsXkd+xQ9Zelox\nyEBCFp1dbQepWJD+wo37D0L6cZFKQDVvEmd3FSxD6RpIpMX0+zbA+MudXxMX\neWptFU3qHbXt7DAqpT3mSBoDDYVVIPSkpe8imfvx1EGEqtobqG1wn0jdyMV1\nHoS6QksZ5d+UZqFSOmF4UouCvUSKwx2j6ddGb8rTL21aEi6pOaWk10P2hypE\nEy7LJXR/DbEKCZ3mh17FSrbZPGzg/gudn/WTEdvb3qXvz5AZxOepraN+GDhC\n47qJd+QMEzgs9b4LPt52ugUnOg2zVDOHXd7308sHSAoaGZnvMZP6WX52AH2z\nbm28q7kUnwJllPfesmrz0HVuqzpATpaqYFDNvcA/CXTqnMUidoJG5pVVSvy2\ntbZ4\r\n=TR60\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBnY2N/GoD40+vfKUcpALSNK9PQkqwgL7SMDFApgvukoAiEAhX9hm4WmdDxVL7jPbO+G+An0kQKVHaDyTWWe/SnvguY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.4--canary.126.37f78c828f378be77f9af3adae78c9d0c888d67f.0_1621839167809_0.44900730490014484"},"_hasShrinkwrap":false},"2.15.4":{"name":"@sberdevices/assistant-client","version":"2.15.4","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b1b98b36eb94a2d8ef63104fb5d44d08ae9384bb","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.15.4","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Fk8nfXrN/FnBj96pyOJMTd9c1UgwK0dxZH1x2E2+sCYedTXN1SdCr67evOsdn0spFq2nusfyMdECFaahX+/UWQ==","shasum":"05d4b4ab2dab852a18c05a711a5ec19d1f45a906","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.15.4.tgz","fileCount":56,"unpackedSize":962342,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq2H8CRA9TVsSAnZWagAA5LAQAIYsSezsRxzqESndEI7G\nvvhN4X+14Iz156vtqvXhTZ6mubWPfEC2NMEC6YFmCosd8wUYD45yodaQJ2em\nYIzVJ5Fxq4opQTpoofR/cXN2syFRALy439xkjcW/LJjXXom//i0NYUN9a4WC\nOIxA3k+F1mpl34tcPXqAHoauZkmxlWrwOJuSlVK9QNNTg4fQ3RHPWFEOyFP7\nzMpdBeImF5zXmyGdri5R/8By0yOcQF4XeCb//f53YSDtekyzshBinqk/Tfv/\niTaq6BbVNV8/FzviWgxP8QyOUq1wpP0Suo/idVPWeg6Z8lVS0AXkBAlBlyXi\n4wmr8RyLG4aDvaEeCSCwyieYaDUkRS5Iv/XWHOa/PbazQS81GnYJ2nMc18zl\nviiu7xE7pU4LINJfUD+avJuO+zPNluuyxT1DRinogvzTTjpwMf/vn9qNdXcG\nyF/01jQGBT1YIsmZK4R1QhMMAEk1dZVfXuslMJsXDyV5vv2ZQXTSETsDwoTr\nzCCgt+A0oavg9SF2VI4QXDzZ+gV/4BeOv7LrTf5kSMybRlSoOO4ULNQeXkba\nuaGies7PiqtdpNcHmg+0mb7kcWYjbMawrDBLhhLOMhq6sAEsOKKWSgMGYQUa\nacA4AChuzyYUoKu9ncCu7aQYRb3ou0gVU5/nzrEsns3btglv4JepkuAFnkJ2\nwj7j\r\n=vzU8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCRPdctYF6YNoDL8fopdpzbfZ3a/oy4NaXqubKDMv8sTQIgI1yfKrIeZhJpx1yhkHuuCty49QnPs7jjESXaq9P4PBw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.15.4_1621844475427_0.5203196328200062"},"_hasShrinkwrap":false},"2.16.0--canary.127.fb4399e83519e8c004a3ddb6d50099d7a61e6a6f.0":{"name":"@sberdevices/assistant-client","version":"2.16.0--canary.127.fb4399e83519e8c004a3ddb6d50099d7a61e6a6f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"fb4399e83519e8c004a3ddb6d50099d7a61e6a6f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.16.0--canary.127.fb4399e83519e8c004a3ddb6d50099d7a61e6a6f.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-XjkC4h/OovWZNAptYD41hTv4yBideUmLeF2NkkbnEBI0ajTTfxLDHKpShAUNi/JVtzac3nK8vzlhdZbzjCaRnA==","shasum":"5fa45fe54418ba81fcb7a39bd7268afe650b73b7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.16.0--canary.127.fb4399e83519e8c004a3ddb6d50099d7a61e6a6f.0.tgz","fileCount":56,"unpackedSize":962890,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq3rZCRA9TVsSAnZWagAA32cP/RdF8NzWzMLBQWPkXU2o\nl1g9Pcy+87nGtOx3Pp45zH3dQXnxyZA2wUvXi5RxGGDuIK1e7eNzwzUkKmg9\n0EpRuYEg1h8orGLxsiQEh9hItud/CA3IavRJQwKlBnD+hPbu2D9aWU4xAHp2\n9KumRvvCzw2plAxUpKjC3hiIi2NPRtEuuIpAgunrba4gtVN8WMyAkA2ROR+0\nkl5Oi3K9n93uRD6MIZVLMgzjgR0xicpym+XyhwZ0AfwW+9MMt84YLbxDIO75\nJwsm4DO+DyAsUJXjtuWKI5InJKjcgb5zPc/hIq+meyQrfQ9VbKOlMPqFUgDg\nbETOERDY2AXBTmpR0yyG6x7ePokALPYRnYWrDLt/Zm4dZhqxZqB8B4fQvsa0\n+YVgKc5vtFdS3eN2UIUXMt3m9emNUT5d+AnjHZCLLRN3O4LJ53JTLX1gfOMr\n0+ig0deRQDJxXHDeLCS1tC9Fr3syVOsUq+RsZUH1Q80gBjtby5yVG/V39ulo\n4wwdRJ901vACA1ygZqbba6eWe51djv9QMsqJDx1SSIigffvhoviqsdstJQTc\n9oqAuI+swMIepJAFT/Um6D41HCikEYspqmnPocCShXV5EIzpd/V7qB7ITbky\nrWRbTv6TPfkUAoooc7JtvZfkj/iea2e0oANHW2sttqN2J8D58tF+3E6bH/ST\nkr0A\r\n=HXBt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGMtIctIonjamToc512tAGbAKyRdXUwa45osOH6iON4ZAiEAhuwYs7U3i4xsOwePegw2C/lwjfdgAx6/64Mkpfar+gY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.16.0--canary.127.fb4399e83519e8c004a3ddb6d50099d7a61e6a6f.0_1621850840730_0.5708045040353575"},"_hasShrinkwrap":false},"2.16.0--canary.127.a9a87128b14f75817aa8ff3ab87e47e7a4b62a8c.0":{"name":"@sberdevices/assistant-client","version":"2.16.0--canary.127.a9a87128b14f75817aa8ff3ab87e47e7a4b62a8c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a9a87128b14f75817aa8ff3ab87e47e7a4b62a8c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.16.0--canary.127.a9a87128b14f75817aa8ff3ab87e47e7a4b62a8c.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-1GM2BNZ/SF+CBeZa4UreKjPbcS5C5Ljp+ntyjW83D7y86/JPRq7+iFZCTJfz1Xn4FQN3lKFFwN/sbGYry5XdjQ==","shasum":"18dacec6e993baeba280597b709928c8c14c1d74","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.16.0--canary.127.a9a87128b14f75817aa8ff3ab87e47e7a4b62a8c.0.tgz","fileCount":56,"unpackedSize":962950,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq4GgCRA9TVsSAnZWagAA4Y8QAI0AKzSOk+mVgfrJ9cRj\n8W9bPfXnMH15RNHKLFVjjekQ0puNUccqYDzm6cDY3y+lsWhql6Qt5Gi4xzx2\nUk6wHYMd0ifqhZTJJHo9x1IrJKfDU+CV/lrpEE9Bj6OjodvjoGjM/V0XWvYa\npdwW9fd/8Ng0hPkUprO0/FkgaxJsccIV7ijQeVbuUjT2VrjnxfqiBxbB11+7\nN86/B8apyKWBgaN+kAzmlfsabE8+5TrBEi2Wp49VWa8nagDSwzY8WbUjZeZp\nNMfLqBckzkLRWvx7PhVojJfNNYRCxH/Xf/iR4s26SiI9NTuSAagpBRUlWO6z\n2AKK3k1rzPmFOCR93FpGL3JYWD6ZHrPIMv3LtN7VuWSoTMKY+tsVxGoe2Tyu\n0cF/l1ysr0g48NyAjcB8U3eFpSbOk+fhrQWQ2607yJKfpeljiVdl8cARvRS+\nhR/jSXu0wckDDsEfE0CqGzDvz6XMheDGWvbf53tHRKxam6VqgL6+xXsAtNcN\nu8CkE8uwNb8mZaY+wt864O+6ij/BIBh9CIjZQoSQZMzlIALUrxDUfW/HfZ1i\nc6MB6aQdymXto6xc9PHuAA7kK8w8Goia2+d/5g1u/8fe7NUcUMkH2/xmcREH\n+JGa0XYFWUS/GVcRiUv0WS3KaJzciP1NU4Hx3FHMSBA7dfvvjE28XnA85IGx\n4rUf\r\n=1QCD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCf2lersBXQdiFHl0LcopET1HqwRmzgFbVJ0XDGpNISigIhANU01yhmpnOiEkmr7TT7eaQyr7ndlSXMoz8JDGTOLyFL"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.16.0--canary.127.a9a87128b14f75817aa8ff3ab87e47e7a4b62a8c.0_1621852575626_0.386943267382879"},"_hasShrinkwrap":false},"2.16.0--canary.127.f9e3ea774ab5da30be6f877e447b66caa5f4bb67.0":{"name":"@sberdevices/assistant-client","version":"2.16.0--canary.127.f9e3ea774ab5da30be6f877e447b66caa5f4bb67.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f9e3ea774ab5da30be6f877e447b66caa5f4bb67","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.16.0--canary.127.f9e3ea774ab5da30be6f877e447b66caa5f4bb67.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-qko+VUlYSPLiAlzd7uCAsGSbGL+bNkJcTCJmBgZiXzyT7BDN0VobD1t1OwjGKz6C0Dq1UEfUPm62FESMhQoL8w==","shasum":"4e6d755a7fe824c91d436647bb11d2726c0cf53b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.16.0--canary.127.f9e3ea774ab5da30be6f877e447b66caa5f4bb67.0.tgz","fileCount":56,"unpackedSize":962950,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq4R/CRA9TVsSAnZWagAAm/8P/0HTbzUSUEI0SnIGuJsO\njzR141P63/FPti8bPl7AfYw54kRFaVoEEStRyeO0aXsPt//9+tQwR+gx1wpi\nRG5Kni71l6ZBfE0881MdY0jOQy36fhBOTOfF85aFBVyPEX9+XOBlqEf7OArG\nYFId3NhY89Mp1F3tmlYEjNaGVKycdrBAPDiwVEZwkPzl8SRq9hPTheFk5/pm\nUfEaBtKI8nvyT8aoVaTzSpNuO+44FQR3TYlK6cAKteKLeUVA3QRtzGx94gK+\nHmXA4ph6A8H340Z3/bs2HKl6RC5ahpujO057XPfqqEwEA7Hv/p0JYbOpLkKp\nECCbqN+2UXbQ8Z+A5QsWfUIeh1C3s2+FqvoukPtpMI8S0o/Gvoa4KaLTXbY8\n96oZn1ZcEU8o6NmSJa3xv0ZjT0uDS7BV9WjZlSRtuvkna46tnnuMRWV5/3Bv\nbBxNd2f1uQtBSKaYrgYNPYAbbhyFc1kVCOT/9TmRyTi50TQ/zjZv3RmvsNW9\nyPhMIYn1zma7jZH82NxXe1zcJvqYL/tRuRabaF64Jqchr13cq0PVT4DKJT8e\nvquwercFY0mz6v8VhNiZc4Tv/RFprDnVbQfY/wc3g3XdZPpblLezlDu120IJ\n9UIMucQhwmPbANb0XHTpHc4a/mqb7GaPRZcosUrS7WS+1jS7BPwyMyn/MqXL\nzVeF\r\n=EWWm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAjQnhe/1vGbvBJowOMJ0R97o5mbR5Ctt0VaEciTPFH6AiEAlLOMeIBhYVs5Sgjq4q9N6G0g6VeI5e8g6BQ4OsTyBlE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.16.0--canary.127.f9e3ea774ab5da30be6f877e447b66caa5f4bb67.0_1621853310859_0.8168996071100687"},"_hasShrinkwrap":false},"2.16.0":{"name":"@sberdevices/assistant-client","version":"2.16.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"72d8104eed01147861921b501e34b62e14ed9003","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.16.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-2910DDI1Adgo2w97jWxcNuQiyYhOeRqTYOK4V5npDP3dOuxsiPuRYrHMRQhwwFc9y016yzjC70h3wh6LxgcjIw==","shasum":"bd84f390c6e486298db5cbce5eff8c1b6ddec1ca","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.16.0.tgz","fileCount":56,"unpackedSize":963115,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq4XyCRA9TVsSAnZWagAAPgUQAJdg93UqvU+6rMvoTLZU\nt97xn6EZ3HLJklWsJkT5BnNY6JzJ1I0f8tnSfn9SsynOw0oIMRc90nxSYPKZ\ntJpYlVqKMzNDMYCmkc20fKe0c840b36dZi3aEYl9dYLMv59gobsNPB6gxwP+\nibIZgVy8GezkGySV+hA1rAX6IfZAMLYnvUhN9uebKOtF0Zeu7MQGkY0vm7pz\nWJsYr4P/6PrDCXqt027Olw8q//d6QsCMzmGjm6jAJldRexWsQFCRuzLlqDTT\nPM6qDE6v8t8RWQykPlDz2wuayVR+/9MtrK7BhUORQmyRsivQ51l08W+oK5KZ\nIdA2/7WD7oftQ+nYi1aBnoKATcrOV6Sy/sjx+P0B0BjnyqdfXwdWe+MvYATP\nhiT4QuYMzRCBTCrA1CglttUMTFeNf0zIH2/YwD1ALoqB0qQEWElo9EVseFdl\nKd3DGfj1+cIFTCqVB48479U+8o2TGiiMHIoAK2TI1RLFb93RVc9vVG6EVIiI\nF6FyepnTOsozl/OLwYUkrDvQlcMt3U0yW81NwR1bUg34NGgE/M7XC01/Kb+m\nNN77aLkJL44Zyeg330I/qJlkPxNBQMP5iCAlgWDP26U6OS2xMYEVCYg0wTDz\nmznOVIgaq8i5+EhglVpqXPHCcI/AtMm+b4GRtrhmhxilb7SRY88iP69wVsaA\nsrAf\r\n=YW3w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAfHtbeNZG6ma7eiikdStGTlG81IEzRaDZc3QCpyOnhlAiAMkmboUXX94gFO8oC1lmAw1sClVq+qQOjZtudqJTvq3w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.16.0_1621853682025_0.15336522282193576"},"_hasShrinkwrap":false},"2.16.1--canary.128.f1a19a9f078f48d7aaabab806c6193cc22afe52e.0":{"name":"@sberdevices/assistant-client","version":"2.16.1--canary.128.f1a19a9f078f48d7aaabab806c6193cc22afe52e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f1a19a9f078f48d7aaabab806c6193cc22afe52e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.16.1--canary.128.f1a19a9f078f48d7aaabab806c6193cc22afe52e.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-K9jek7aARsH6cDA2C6Zc8raQIVZbzQ4L/Lq+8V6pMRy/GRc+lVEAUbDr5LVbdUgBKkMiG69VaJ/qT/qJu0PJ9w==","shasum":"e1dca6d750879e53c7cc2ae42d3a9a48e43d1f78","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.16.1--canary.128.f1a19a9f078f48d7aaabab806c6193cc22afe52e.0.tgz","fileCount":56,"unpackedSize":964252,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq7zRCRA9TVsSAnZWagAAKDIP/0FXW34arjY0fRzYeMql\nauwEjuu1QjxKiMWb8/QjkmEu8g8tMoJl52PRXCXpMF01CCHGOWWq6x5yG49/\n2YJcutrH7MT6HmA/Z/Xhp6IBh6YJMwJSZ3JKMlbr9aQwImo+kU0oFqKrfyWh\nJJfymejrK2QNSkx8lCjIwptV9zg9F6XbehQGS+D2rEt6g8p3FjAhc1nEAAi2\nwT+s0+5vLtahovgICYGyFroErwVrCsJXY0TJHTqguRe7H40PV05x957F6PvM\n9bc49oYIyQMu2Yw6NpPFnQ+3pAkDaJ1osVWXTQJmiszbckQhBynyo0JfI1bi\nUWIleK9pSbuYs0JFhHkK3xnF951afby4hyTDxrG3+GNJP5kXmKXq2l2sPEVD\nbDfJva886pGdvQOpLsp0HCfmRgDpurmTR32Leol9qXnSYurMDPq26A6vZiHY\nkHGrGPBAwXBj32Z8eUkb7G36tMsC5BTZfpVcUfffOz2hxgkTGS4gQaFSK4wi\nqshOwJmzwf5Cb9bMODkQPRiADX+4f8Df4JcKGXeo9QIN1S03A/RLYLjDrz0p\n55BjgJLEIcFCZ4UGSwCd0fbnXKctQDaJFTL5a6poVMZ4hZkLNlchMLqWlcLf\nNoPYouyQlcp4tLIP+Gk4HgkokSj7qh+sy8ZTUeLSt8xgUiac8F92JXBnkniS\nya5e\r\n=a1+o\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID7jXwuK81/mJFtqWmRTP0pBh8pe2piQylBob1Z4pfmNAiBhWvkLTGxn7ko60MmVnQVeU2L9yvXO4y7mP5vzwFL7RQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.16.1--canary.128.f1a19a9f078f48d7aaabab806c6193cc22afe52e.0_1621867728716_0.8080109110799771"},"_hasShrinkwrap":false},"2.16.1":{"name":"@sberdevices/assistant-client","version":"2.16.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1b7c3409e2ece6c9fc2c14352cf2b60c9c945fb9","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.16.1","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-WMpwVgyRU8XHohBflNkNxg8OX/9aKFHPaeQW8UVM3vNLvW1FXvaMkbnzaJVNwNAgCWcIRiiewlXwwfneE6TNcw==","shasum":"e7189a1214919b29256b75a666f73c2a183310fb","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.16.1.tgz","fileCount":56,"unpackedSize":964519,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq8kaCRA9TVsSAnZWagAAA3QQAJ8NmxgQCxm0XL6AWwph\npub8XIGMuizueCPXrAZBdyPqRsCJoetET6RqafILar8qqd+5gWlPILMJ7XvF\n5Iiovwbyx6Dy+szRExetGDbbdU2Ce6MKo+4Xc4fnfxH7udH+ZiikRymXpskP\nbYW8YG01d/FwRqGHmFgiGlZ2UdKDdzmq5GVCYXy8A8SXAbeVvnB0Ai/5ArUr\nzNyj7P1cHA+3UObY0DQMQDdYZRtVIkV2gGbVdwRh1DA1Ow11lcYqTzu1ZA7f\nA7qoR4w4dc+DKbLJR/jzkfuUKsJB3+F6u/kciehPrO50RRn7UcuIzNCDXUDR\nugHcBQJVa1UzntYfL+pgs7FoOQMhA35dZBp/NlfyddZwCwEBps1EfqBtxS1d\npq2qGBajoCvyUn+ns45pHBuEz00rDL+/HAgVjSQQkUZqehsq5VerUu/Ip0b6\nzq4rGqQsFQIyUAnT80NwdzDk9yk3XQtiWEYpo0fXmjPDpcSi29lDI517ExAo\nWkiyCHp0+RDz0UmAD3yNpySXMQzj5PxED8RDbFFnkC31mW1RSe6nhxql/I8a\naUFhwnnuQv6QG1eKwHJ80llJogBoqmhe0hne8KJ6sD/Lut9l15MMvr2/y9ec\n//T5xE3wRmqJR8jpJbscBbuLA2PP4mQAGDMSf1r+z0Hced8uJw/AiMyfT+P2\nl6fV\r\n=WcFm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDI2u4W5AtfdHfc8XmtlDIt/4dfCnteYtNKN+JydYFEPQIhAJjGkGR3gBM7SDO4wYl/atUECjsHLF5p5TC9mfL/++gv"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.16.1_1621870873567_0.6159845656030467"},"_hasShrinkwrap":false},"2.17.0--canary.129.dae0f0fb40ab1b7a1a501f7ced719985fc52d3dd.0":{"name":"@sberdevices/assistant-client","version":"2.17.0--canary.129.dae0f0fb40ab1b7a1a501f7ced719985fc52d3dd.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"dae0f0fb40ab1b7a1a501f7ced719985fc52d3dd","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.17.0--canary.129.dae0f0fb40ab1b7a1a501f7ced719985fc52d3dd.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-3ggAoVKVc831s3ev64OJFibHzXgVtcbxRxaBRU5ShH+62IEPa3AqSKZU3OsMX2YabY1quAfw1WNTrLyYm+M59Q==","shasum":"1b6535190ceb19c7a4dc925d08e1aae0d9a03f4d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.17.0--canary.129.dae0f0fb40ab1b7a1a501f7ced719985fc52d3dd.0.tgz","fileCount":56,"unpackedSize":961421,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgriZlCRA9TVsSAnZWagAAnjEP/2J9g+S+iv6lx/4sq1km\n0VVsV/SJMRzZjpevZ6Sjp4MdAMHeFep5GxrfES+MAPUBirgPrlESnt5LYDpb\nnZbfEKUgDipry0KvFgnRCWhVEAeG8q0HGvFC4cJwzCKw6Jfm4hLVd8Wpsthb\no2vaWBGMzodmOTT3tVr4qkJ0Q0JbireL5dgrRrFgeLcYvcbr4UKtarG3A5VJ\nJ1DxyhEOK1K2s1BVk3JAdtObwX96G6jcgmOK4fY417dk4hUwyW3y8Ui6dqu5\ncKeGZ0GOdsIBfXohIrypHgtXqH8WK2VSQjjHgJyANYCuiXdcH/n+4uDvhA9z\n9IwNAQj8MHjkDodsL2Ycznz2lxNmwHg3DWVrA7tKcSn7WjjiPLSVweq67OhM\n8gSyzzAoHgm2dW+/TY4l1PuMZ6dxD03ozfEk8eZiewulIki9P2rJogwVEToR\nI1/wkTAYdaNNU+lTM05kLLUZSNIkqRtfAvH5l3sU26qZZoHIt6LHAu9aTYcT\nGgrSOYNkTPWjyUAG68QPlz+fv+1SyWWnIOp050O5UnO0rU2kaIuAQY+P233p\n1PsZX2cQWcksYj6sAECnNDxRLYZi1/c0gAvn9ABCX5d1YERGZsnlkOcXwiKI\ndE2poylCexzUxhi7UFLRN0PvW53aXmihPoRDU2jzZOqeqV26urgpfHq59+XM\njULV\r\n=jihF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUYlVygG6Br2xIiDNjV0mQuQViDo3TNg6EX35t67sQVwIgN1jwjBYVFdj9v8tuha23unwyeBDPHtXTs+MZasFYpFM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.17.0--canary.129.dae0f0fb40ab1b7a1a501f7ced719985fc52d3dd.0_1622025828796_0.7770649484618626"},"_hasShrinkwrap":false},"2.17.0--canary.129.ac9c09aff4a687979ca1b730c40ff5a36e8bed2d.0":{"name":"@sberdevices/assistant-client","version":"2.17.0--canary.129.ac9c09aff4a687979ca1b730c40ff5a36e8bed2d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ac9c09aff4a687979ca1b730c40ff5a36e8bed2d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.17.0--canary.129.ac9c09aff4a687979ca1b730c40ff5a36e8bed2d.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-492yg+VHqwVxElH0mxz3pwqFH3grC225nO4ufUnIa74qr047ARjxhsD+XX+Kyxqxb0AZa192u+Z73FlXhCI3xQ==","shasum":"68a48516bf3dfaccb63b044547378c11392fe6b7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.17.0--canary.129.ac9c09aff4a687979ca1b730c40ff5a36e8bed2d.0.tgz","fileCount":68,"unpackedSize":992817,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgt06JCRA9TVsSAnZWagAABbgP/2YBTrc1lmW/wjUsOpUR\nXYy7ZdN6TZ1KQjvjmVk/Illiw0KdOylgkmnKCqZHd9+UFUNMhklRxJa+cN5d\nGueR+2UT9CaX8wuZQpr//77Zg/vLC8KuNh5E/TkVBM/FfPgZO2IHJoOwCGSn\nsIq5J1CI5gvwCUFydwV5oaAd8XIkSK5dCquxts5PCk8vGcasNyK5WQytMD1H\no+FTTWkqzlxJ9zGkKy64V3zR5HH6S4kntG0shgIodIw0FreR0nEBU2T0sRpk\nm7MDdmRKUVGgKRb1vh9aH4Iq3j2km0hbjwUJsNe1HgS/pX0Cpy7UOt12gGD5\n3h8MayQre2cdXU06BUrlj3tF+Nvl4GtruWKj2wEW2Ucv9tqfwleP5E2u4zek\nkVJfmYNqLkSN+r3CAfOq8UmHNxeaOI1I4gcFEj8of9VqVX/C0sgLAcn+lTls\nchAd533HgkbbmdoQLd5EbQ7GGlO1C4nmlxZSX/6OJOPIphZmPvJ4GbcceUyh\nKHThciPtgQgIEkayMwSg5WPyk+1BYEF6lSEDkKRm3e43r4uIVUQn7S70Rx23\nQWedCSedjPyWa3GBC8NDoQ2OOPOxtF6HnzjvlQDuNG3S4M3XUzEduh+j2aPI\nhv9kriYLdURwEHtmkNzRBCS1pnuN4L9qcb9zSl6+HA+Y1jw0K7c1MxIY383r\noNLj\r\n=72FI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDpAsrY1gq6rTmFl4dm6cqFLD2fME01PXSguZA0DTYXjAIhAOG3CMyVFq4CFNAQA82tp5lRCAz2aS9idyNigeTCqt9O"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.17.0--canary.129.ac9c09aff4a687979ca1b730c40ff5a36e8bed2d.0_1622625929334_0.18371185235298126"},"_hasShrinkwrap":false},"2.17.0--canary.129.7a4ad6e6912326ce24a9b34abe5284e578baa7a7.0":{"name":"@sberdevices/assistant-client","version":"2.17.0--canary.129.7a4ad6e6912326ce24a9b34abe5284e578baa7a7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7a4ad6e6912326ce24a9b34abe5284e578baa7a7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.17.0--canary.129.7a4ad6e6912326ce24a9b34abe5284e578baa7a7.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-IB9zQNaiZMBxjEimNlok2WsTfd6SSa1ClUCvLwmXcyQWu9XysZrSIDB+aXjLCRxQhgqdTalcNpm8O8yK8cv/9A==","shasum":"60d35ad25a8270536f4f57e8dbd31d90563bff36","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.17.0--canary.129.7a4ad6e6912326ce24a9b34abe5284e578baa7a7.0.tgz","fileCount":68,"unpackedSize":992989,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgt1WrCRA9TVsSAnZWagAAmFUP/AhyhG/IfLU4FaYg1E9j\nz2cjNby/kLIzPjRJZrZKJZXmt6UaSVy+XX+MIh/zLnPQ+t5A7MnjAuZbrRMA\np/C4vUpHDNuUryPGPA5y6fovR9Lzsv5DX6ruMACUK5zOdQKa/JUYO78NUbqa\nMTgNyEpXsbE5d17TlgKYzluqdsonHZHEVQ5wEFGu/BzW7CksUCBW2gbyU4y2\nIq6I51xdMLetZETe+dsi1bIiFlGpxWIrmDeGAMy3c2JyGAIqjimMDb5eyRIN\nzY1DhxvG1F4Paba69b7NYgzcUTEBqL8Km5qVKolF7TWs0qvILmx/eu42s6jj\nQBsAjnjvWDfM/LMJZ3CaM6/WshVRF0l3IBJJJ5uX8Pob5Eac1BBsg0H5p9Pg\nKprEZDHSW5eTztszyNi6xwr/Xe9R0K52CLofVWiN2kAQ75ak3CXt1/KEA/Nz\nPd5+9Ftx+5Y2wQVCWXwpgaGgAX5+evgU+9WTcrWT/z1udhaqPOYkWih7nwOb\nbJpaJ1pHy1eZ83rFHARH6hC1KlQT8aQxk4I75QBjYuRPHqvWq00sZT0e5Jg9\nr+sQNUxqnimKW2D5LcFOBxKHv30DBWw+wdC3wXN5i3/V0ogl9Tx++y1rawF3\neniM8zo9EfI7QdUV0AE2ZMIRgEL/XT7yXnYwGCSnPUgwHDmB2+iA5uXzJu4G\naY4b\r\n=/n6T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIANSmJKEED+Mer57L7XcjV9H274BqSoposw3j2xeaERfAiEAomTGNF4YwShslGgw/+AKQetFSZ6rxLO+gJApHCCVT9w="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.17.0--canary.129.7a4ad6e6912326ce24a9b34abe5284e578baa7a7.0_1622627754704_0.8073359781162075"},"_hasShrinkwrap":false},"2.17.0--canary.129.0b36f7510a2707a4b3d55f5b453d2f4957ce69e6.0":{"name":"@sberdevices/assistant-client","version":"2.17.0--canary.129.0b36f7510a2707a4b3d55f5b453d2f4957ce69e6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0b36f7510a2707a4b3d55f5b453d2f4957ce69e6","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.17.0--canary.129.0b36f7510a2707a4b3d55f5b453d2f4957ce69e6.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-PXYNeVYwMrW43lqOU//gJXX8sk45l4jm/MSO/xhQ9+SU4R86B4L/1XW/EXOBmNsuEWJ3OeD34ftmTIbUxe9Zgw==","shasum":"1d766655a2c9796c7046a15c064c47fd0abf7042","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.17.0--canary.129.0b36f7510a2707a4b3d55f5b453d2f4957ce69e6.0.tgz","fileCount":68,"unpackedSize":993564,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguL//CRA9TVsSAnZWagAApW4P/AyqT//S3q+Xrdh7YdjC\nCaOcZ3HW5R5RkdxAMNVXfpsKd1f3Wu047wjI9T3JaMNhQ3CLRKrWmtvysx83\nFyHhQphwkAjR9uwmGHi3ZyqOCyKKUtQUkHLPrS6hjqgCOnbo1/9brjbaPwKO\nxmX82del1t058Asq39LzZMdU2Ay2kI3XYpKUfCu5cKinakbzcT+R9eN2LwrI\nWWvbRrcAbPDEnoINRfbcJRQ8lWgsArMGqDqA65OsbhPKqqUzqW4nadoMODme\n2uFun9Hc0jcJ/oWBwFx5ZeaG8GfonQxx09azsr/UdWoDhj6l74advg/jb8lC\nwwidH5YJ9j1N0EzGvd3iZ4AZmkVISsdmVmBuCVb6fpS/mSomjB/qu1rmh78g\nHkK5zMuJv7PTtAGYQXQ1iu0hHlM19IekA23ldAK2nHQlkngk9aa+dIvQs4fp\nxqYeKW1mkP0wlfZOmhxhaN36s+Friwdzk7DCf7FcGFlvzp+gr3ZUcedCdCwL\nNy3fEVpfJUuu9g7RSpESta1YTXcwn70zsP5G+r+WFGXRKkp3Sgq0jT/oroop\nZQJypknJ9hZ1RIxa0dKM2Im0a8Z5GiaRCBsp/CU5JnF8XjA4TzCqQ6rckmsJ\nj0BmdBWd8z4sjep0m/m0ye9T5QGl/yxMaQp5GYOocv8C09Oli/jIVL8C9QVX\nfgtZ\r\n=Ut8k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBrlEJnUkCbuV6dSmYOgFoRXrW9PNJhWgWABU3YIH73dAiATKmfwiGVYEyuJ/Tjaj6QLc6g4p6jqzI8FGitkIv/Jcg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.17.0--canary.129.0b36f7510a2707a4b3d55f5b453d2f4957ce69e6.0_1622720511678_0.8029853504605131"},"_hasShrinkwrap":false},"2.17.0--canary.129.4eeb7fa28f3d15f823cfec7b49cee1a59c220176.0":{"name":"@sberdevices/assistant-client","version":"2.17.0--canary.129.4eeb7fa28f3d15f823cfec7b49cee1a59c220176.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4eeb7fa28f3d15f823cfec7b49cee1a59c220176","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.17.0--canary.129.4eeb7fa28f3d15f823cfec7b49cee1a59c220176.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-lO+U7Q1loQeGxyIW5WItS8qN3Tp5HBs33BsQQeBv4Adfgswx9+keGdlufWHbhf/7/C62tNmaB9W+hLxOMdH99w==","shasum":"818afb2ac309e52d2e3b4490a18e0bebb47d7952","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.17.0--canary.129.4eeb7fa28f3d15f823cfec7b49cee1a59c220176.0.tgz","fileCount":68,"unpackedSize":996082,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguNA5CRA9TVsSAnZWagAAxNYQAIpSipOIWEUnb6IfHAAb\nHG0Z6C5L7686oRMGH4oWPU4hd7NYq5OCFVqI/ujgoPJ+cMzSE9J8S86Fv8v4\n2ZsOAbiMADlUX7O96f8GUbXEGBUfU6/a57XOGz5zXl8AJQcqM/FrtLpEIxF1\nj9DbH+AMP9gqLQD8Fq71FSHca0HZdXH1XC9lqyqNIJUE5RrE0lBa9xxF976A\nTNmW2P8aguGhHdUQmn0ZUcRL/nIJHwBc1xqqOtnX40plFu7bzQptLNwVoX5E\nqJEV8mjj0fpK+YSHJQmvFP03XYIs7rsWVyT3KXayoaktH4zuao1Q2BNCb36G\njn/i7eaXNGP1LxgvbF2hFfEr6uq1CEOW7Q3fhhAY+LQfghdnUHh7SOe74+ee\nxCBoHdqLBPPOjZRa28JpYJNiCYr1zjmwiNFmAodzgguOU4LAi1mp66W6cKA8\nv10ndEg6C8WFxUz642acv7qfAUpWkhl6qlJ6YJDzAftqy1JH5iyRNqTPzv+I\nshrN6dnhdu7f1kytpUNJjPgFVJa9Pxc6TuuoRROFBiv8qi4gjGjLaK3rw5SR\nIxs48giJdxdnuJjZt/12YSSRKMKass9spIfWQngBysA7TQ+KMK68dcHdH0Z6\nsKJ4uo5vYuMoCTvLV78rW5X0cmDDSAn15FRyLFhiEyurIQuu1Ge1JhuC2EGJ\nNb6b\r\n=CTkS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCfBdzRR3DR9eVvqoDBQVlNI1FUuFTdQKiGP7OAHEngdwIhANtTDVp37pQNCnYSFKzXGU5A59CMkw7KCakbwaLksTL5"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.17.0--canary.129.4eeb7fa28f3d15f823cfec7b49cee1a59c220176.0_1622724665416_0.29371656027635606"},"_hasShrinkwrap":false},"2.16.2--canary.132.22ae2dbfbf62648c00068744e3006eca33e8959c.0":{"name":"@sberdevices/assistant-client","version":"2.16.2--canary.132.22ae2dbfbf62648c00068744e3006eca33e8959c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"22ae2dbfbf62648c00068744e3006eca33e8959c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: AssistantAction | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@2.16.2--canary.132.22ae2dbfbf62648c00068744e3006eca33e8959c.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-yEda5Lk3CG3xvMVodHj1PT9M0IaA9SamYPhK6OjTrg5VjQvZzkf30ZpTBEk5O4twDreoKQ4DuazWi8jn53IGGA==","shasum":"230e398e6a7b9bfc9ca4f9b6573422e4f9c2cf7c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-2.16.2--canary.132.22ae2dbfbf62648c00068744e3006eca33e8959c.0.tgz","fileCount":56,"unpackedSize":966466,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgux7lCRA9TVsSAnZWagAA99YP/1/Hqvv6T567LFVcjxXp\nA4I9UGCUE9tTVSqE1PiXjlFYdsKyYiluYW3kmVlQDq4K5iUnQst5UJTXTdah\nEfOTUBj6pgxhIY4+k29j9pHhOnvfU2ZmovHL9lwOFw1kypKYvLlRFavzoh99\nmFg9vNK6xN/r6gzj1BTz3O3F7O0IoG6ApWUlIZ6xzGsLlXKZYEN5dLhiqRhy\nbZEjhvPA7HxoNZjStsb/UTg6LrECTiSb9dgZOo4T3T6EpV6VclAs/urQ5pnK\nvl1sFLKKDQ3ebVoCGtiTV+KM9G98r8wotjdxmKn/a4iPOLIbkEp/zQW9t/uO\ncVITr/rQlQ5KbK4IZs5KGJsB4iz4FFRBWr6z9vzUg+Qk3dKFPeOXDEmInTJt\nW7Sq9zOFCKo41i0vdYYEI4m0NgXHMOFk5vaZKVR4bfVXCxuYOG0L963smgt1\nPlOfmg7FRmVRZ5BkCLfStpwZxRJqepIGzl208wMRujRSTYwi+Nl2sEbDp2h+\nRkaUJfbKRtXIO0F0MU1HnwEOMjhvXRs9QQUYUcqalyIUZVN/UIVpPP7mUYHd\nPjzj155+9msKwpcfR5onLqFZFOCsEUxP2nm0jlUU/IKvazm+HKQf/GnRFpHy\nRd9d/p68UBzyj6AhpTpEO46w9R9tVCR/XVUoWfEuARvcdgUohwYToc7xBhfV\nnYCA\r\n=JvTb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHn0yWl3VkV3nC8f/aTQ17oCbktxEKxRgOs5PscQje9AIgNS+iWyJ1TT0l2bm/9hDTDvo9hNAGJ9OoGyD9/2CDT4g="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_2.16.2--canary.132.22ae2dbfbf62648c00068744e3006eca33e8959c.0_1622875877778_0.7710424377000222"},"_hasShrinkwrap":false},"3.0.0--canary.129.dff7486040835b0ed9349c25ca905b0c5c5ce55e.0":{"name":"@sberdevices/assistant-client","version":"3.0.0--canary.129.dff7486040835b0ed9349c25ca905b0c5c5ce55e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"dff7486040835b0ed9349c25ca905b0c5c5ce55e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.0.0--canary.129.dff7486040835b0ed9349c25ca905b0c5c5ce55e.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-ofSGr65SDIu9jF8lngFnYyjDBHrA7/dTJX++HHB9ZLOm2NXEJttQCkIEwxpWqVXerPz/oBfqTBTWxgZFz9+HbA==","shasum":"fe1add5cee1ca993714efa61729d1589f4150e3f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.0.0--canary.129.dff7486040835b0ed9349c25ca905b0c5c5ce55e.0.tgz","fileCount":68,"unpackedSize":996085,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgvgeBCRA9TVsSAnZWagAAf70P/jJ5oUtnI9zPg1tBJpnU\nssjDka9nOzTQQ3b4OcbuA+WvQevOiAmVngRmlzj4kqlicmWNJvBFF/JVT1xR\nhiqkqujyHNW3wDF4cbCvpw+HUC8JgUZlLFzOeYbGsWsUgkr1PWuC83hBWr2C\nC64TC4zw6pC0sUi79Z+b52iJcAmXII2S4zJXxHx53OKreLMHgnHfA6K54Zuw\nA5gpkoimUz+tGUXs4mgxQsoHr6I1JQRyj4sgEJzORANwV/vP9NYsLR1syIfj\nw+gow6CoTQ9SMeDEQXHHU0QT2dcUN8FK9KD0xwck4YzeD7zCNPZrATnuqI1G\n5rXHGixspGLrHi1gXDqjB6K9VlNRWgvDwKNwhu9oD3aZFF7lLnNqgnn/z0c9\nBK1PkwUOKUg/wApkMHdciz3GVXXe/FWKSgh1tOgZi6scxj4Wv1Is1f0JFC5F\nCyRk99M6UipsE0iexJYifd1BkTzv5G0Ol23aVp910IGi6hWlio/4/s3reSSL\nctXh7OpYA3v2uzpNexr7JSZr41viZUHMj+v3DHqbMVnYqmk0Tt2AZIY8SFke\nlcbfprRyqcG0xLcfNqQLyQOiMcys6e6BBMBS7M83pRz+jYFDg3LX2KeMlsIt\nGbL4WyB2Nt8LlQVbQFZ/v2wwab7DlcbGS7cuTD/fDT1ib7DMxG9o/Se0V2Hw\nsh5H\r\n=2Ghw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD7bH91kyKqfF/U6U2lv+tMB9kf8V2Xrpv4ktRsvkDfwAIgGIba6LxU04aiodj3c05jWv2PYbSU0QTodaQmMIXUjoo="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.0.0--canary.129.dff7486040835b0ed9349c25ca905b0c5c5ce55e.0_1623066497085_0.7235560111618466"},"_hasShrinkwrap":false},"3.0.0--canary.129.b493b78020a5a98d998be32a91184037d3dbaccd.0":{"name":"@sberdevices/assistant-client","version":"3.0.0--canary.129.b493b78020a5a98d998be32a91184037d3dbaccd.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b493b78020a5a98d998be32a91184037d3dbaccd","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.0.0--canary.129.b493b78020a5a98d998be32a91184037d3dbaccd.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-tbGrRW96zbhJOn9CMHUc/hZEu5uaM4H9MLvtSh6lsjNo7Nsg4Z567ympWrjOjgzZpNqPpizeKoMvP4mtk46zQA==","shasum":"42a121d28032c0471d407dd682dee5c94bff3459","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.0.0--canary.129.b493b78020a5a98d998be32a91184037d3dbaccd.0.tgz","fileCount":68,"unpackedSize":994776,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgviCuCRA9TVsSAnZWagAAs+0P/3jg7iXsXNwNYGsBDdY+\nuKkOCML2MXWUM/id+BsQlPmxWWRFn+MEQSbobNrCClKc5v+yk2lIv7vMiUz0\nA61HRTHurZL0sw2/JaChJ9KIwU2xB5Qk4u8h/eOmwVsFbCumFyqYMNkg27iN\nsZtkm33u6/XTyxegu7wudACvu/E4CdQhXmMIvubFbpc3PgusD7OJVIIA//1i\n7OwxY/EF15c6Nno4AG5yStYHZp1csA2TbAsz/WCj44pc6jhsJ6ZS4xCfovVx\nnSPYLo3pQwhs4zxiDhPfEAL7q7IpGha6THgeyADrKh6ktPCGCAaPQpnvnpcb\ncu85QblX6VShri4H+KkqperAcnBXVPJkenRnSx4VsqSKDcIiv+gnB16yjh0h\nEFFsX/HjpAbPM0925FoMl/0T/rajGiVdu4eTmM85/r9xE0L8KaVzwW7lYIdN\n+/1ySoScKucem4xhYcJ8wUZMQqZrvrJ3xdG3cTFz5w2RikGtHuR0EdB6bXCV\nZR5tVFFKNFQb8CRJAnC1kXpHBSPZAJxNF/lZQmE0B/bgWyvJtoHEFXahCb93\nN3xRi7CRBwczi8Rjl4QdBC9fR1r4BJwPS9LbdoSeFbO69VTVrNF0CYCPmdCg\nT2j1e0xJO7IzDbp6Gpavra4gkb8KEWzpWVMkR7PYrZ+bejZ52coBB1IlqO3q\nLxh8\r\n=ckq4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCAyNuh/gaoKj57OXS38Kev0cqkF9P3/EdeiUz52OrqBwIgC0iOIX/hrl2SJ3MAdTH8yzLETJfl/Y+3Fpg8Q4wJKVg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.0.0--canary.129.b493b78020a5a98d998be32a91184037d3dbaccd.0_1623072942129_0.14182716239412918"},"_hasShrinkwrap":false},"3.0.0":{"name":"@sberdevices/assistant-client","version":"3.0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"bc8fd9eb3772140f03f5d4394f7a8ac2e91007a0","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.0.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-uNfqr54hbvr342mr6GHKiJO1qpfQCdjDOPRapPfH+r0EZIkqRGET6xL6xOYVPRGn5tE+lTNPGKn2P2XKMGulhQ==","shasum":"ea5b251e75f7cd41972fffe3f7a0b2aa5d8f1479","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.0.0.tgz","fileCount":68,"unpackedSize":995284,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgvitsCRA9TVsSAnZWagAAkvIP/RnC4ra3Jav/JNxFGUQ8\nrTuOZ9BqfRGttorWXM+Yxjow06K4U7S748ltarKEREhVAlMxUD5RI58v6igI\nI2CLZpwEM9cx4RdwXuG9hkdYTuiXq1uVKLHJGrZ4f4W+eRpym3whQGG976/F\nK8dnFczBjnpAY7INZa2lZygQszuQPr4yEHcuFjkRwiniu7Dk83SGtoDO0688\nap4hKa4l6HJRYhTfDgztvZ5wrVqBrRKl4H2UZFIkwZqgy7MZNsDV+1C5MOat\na2kEBidBUlmRYYm6RrzM2TjO8cuyiR0CIMQnRbuMlviaS9VF1eRxTyGWa4ZN\nFF7g8JXHAi+KkdYHqJfQrwgodZMsYeasBcCrO9i8JpI6+a5OsezIYJxQLZIh\nrNTKF9xYuPeeM5bL36+1fyhGJOQnhF+/FhAMR4mqCMB+mgjp/zlTv0oJleMh\nP7CKvXCXdRrkwVHvvBwB87zNDL+35Vqsar3G5hzCyL9/YhdI0fgeJFGaWAi1\nZUT2H6reHts3YjXfTu7gMbantzFLQwTIXTVzGS4pgvcADieVD9OGFMz1+g3b\nz8KDY/+xaiWBOLNz6VUFSzEfOj7XheDoTYojgddETgz6r6CX83O/KWmKszkE\nLbURC/6M7zySiUKNNsGp91i+83/P6SrHfOeXxkl87qmv+jUL2rjGubrKPg9O\nwUF2\r\n=DUjI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFWAUXyhbG4Tlj9PZWmQuJOgSG651IwmeHW2U50dX4I6AiBd33SfrPEH3mS1UU3qcvd+bc36KxxrcBFjLbv1+bYo6g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.0.0_1623075692868_0.9185603647627338"},"_hasShrinkwrap":false},"3.0.1--canary.132.a7f81cc1cb8b8e953734b661934d13f807c37ddc.0":{"name":"@sberdevices/assistant-client","version":"3.0.1--canary.132.a7f81cc1cb8b8e953734b661934d13f807c37ddc.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a7f81cc1cb8b8e953734b661934d13f807c37ddc","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.0.1--canary.132.a7f81cc1cb8b8e953734b661934d13f807c37ddc.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-VWYrN3OWElSMhEB18fvDNrhvR2MoqK2dsanN3JUJv9qV07SFO5xibpCT2HJbmLhHU17Buf/POQ8TmqUE25JApw==","shasum":"412039fd315910b87176f35c441e45e3b718b554","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.0.1--canary.132.a7f81cc1cb8b8e953734b661934d13f807c37ddc.0.tgz","fileCount":68,"unpackedSize":998645,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgvjzTCRA9TVsSAnZWagAAp0UP/3vQ0saQ+or6v1ccCZNl\noU5Jn9ElUvmUT8s6kt9A2Gq4FhZzMxAWzrl7y5q5ORS5HS/w7qrTAA8FTDw7\nmszbTYCjtfrNheY+sJurmXvSKEPuQSnMrNsMreKMWOSfRAUo5AEBG11bCRif\nGIsbzyUdpbiWKRA0c8otP8g31D/ko6VNX4OIBigvJWnK/3ifQxalXmS5VQ4H\nLHE6tiokjd2AhifyB642Omkm/e77pI3ttvDCUsBSqL623JB5TEblMcnXmpVE\nIXZSXX69qot1RJpU/Ru1uacHZXhGRpSOEOityms5hHe9V7I7QIurpVdVZAUi\ncJ1q0R6wo9gt3ZajRJ3iEeXsElxi7BdG76UN6uZX/RnKFimGEI30IZ/uYHPL\nvcG5Fs2NsY0lJyR/XoCvV9/aRb+GxPr+f1ynDFJQ+Nm6KSy2TMYJ4nO+loWM\ndspidxrHEe8ikWnNnj55D2plqfTCvScUciiQLShfwxWFcw6jyxzE7VQWEVwF\njn9f0QhYsk76gy+jP4/2qDkaSINJRqu32O1INxcWKldm9D2jRhw9SBgkVnzK\nOZeo7+E6OaOSSOOUBYAiQMkNC/wCKGJsnoNkAvXdEhAlaMAUcsCtNCf/KoJS\ntPIX/k0B5TU65LnPccjWaj23Ivd+Q1lXPx0K42+tq74Cz5ajKV60L6Tt2fdb\nfxMm\r\n=5O03\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHO45oEuA+umFlE8jnZbIncHO+vOTWEQCrhyxwfS1RLwIgOqgZ6r4FovaZFpioPnook6DNZtaVGoRpwB0EyGKgY80="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.0.1--canary.132.a7f81cc1cb8b8e953734b661934d13f807c37ddc.0_1623080147559_0.9010974653463746"},"_hasShrinkwrap":false},"3.0.1--canary.133.9872db671e7586f46d55ec048488eeda780c9b17.0":{"name":"@sberdevices/assistant-client","version":"3.0.1--canary.133.9872db671e7586f46d55ec048488eeda780c9b17.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9872db671e7586f46d55ec048488eeda780c9b17","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.0.1--canary.133.9872db671e7586f46d55ec048488eeda780c9b17.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-zUPb0RtMpaqyfKmTApk5i6dP6p9lLm7foTgyi4FIyMc5n3FZdiyn+Z1sYTPXYNOIUUqICHNcKeg+JmM7NqMzCg==","shasum":"6a0556da9355e48fd464436829bd7f161f295185","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.0.1--canary.133.9872db671e7586f46d55ec048488eeda780c9b17.0.tgz","fileCount":68,"unpackedSize":995261,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgv0reCRA9TVsSAnZWagAAPUoQAJIuVxLM9iUU8Qc5FxvF\ndnxDBYFZ5qwzVazQexSxIFArMf3sm3dReiBNr5PN1+v7n9M5vwU81GAmJ6KW\nKNaTEtQVtccI7Lb9FAnq+WA1JNxb0Gd8RURlkZ44Ol8MMAHzu5Nw2VLm1hkT\nIQOMluR5xTBWM1omqjLdlH4C8iLiv7DD0OY0nfsy+AXm+63/SsXvQB/p/Hzk\nJ5WN136O7WPAUxGMjaglhGB4xBywFUvP78xqU/uHR0nHH4aDsdHcKcPN+OLA\nDjmfzAcm0KEjY9jWSftHycfwdcovZsbFxC0e2Jdp0/Qvt3J6/FcIHYu97L2a\nIv5OkLjjFZEmatCnCXniREzHzEJYSPvpcnzxnutUlecmlQAb0bmKzjsaktD9\nhRbqBe/DDCZsVt+JU8guh7EzI0mirkgXad9A03yDAMk3J/Mi46xCtng+OAMb\nzcXyATLrqO0kxEg9e2Eda2P92/TI45HgR9S/b1TBMGGRKIxofdtmWkVTcGkX\nprqs5t02ov7f8Qg3jCMiplaitURGXXpMInhmDco6fnitFf1ZoPTkTCu1/35K\nEXbdUIziDaIGgvv+75UPH4Z8KHhQXnnyIuBvBXyqDgtT50WH1bWwVCP0Tnh7\nMPbREGbCGInKtm59AyGmknwbA8r9qQWAGZlEwO3lkDefhbNRZR8MwNwOn3mZ\nA5Az\r\n=dz8y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIADf+HCDEwl8z7ExI6nDXRyg90ij0zslcrFuWufEMq+9AiA9IplIprQrTinNvVxA7h3qJ8dibP7jbSyRn5kwI0zyog=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.0.1--canary.133.9872db671e7586f46d55ec048488eeda780c9b17.0_1623149277879_0.9507992159906247"},"_hasShrinkwrap":false},"3.1.0--canary.134.924b11e784307d463503e4e2b41f79eceb6b4396.0":{"name":"@sberdevices/assistant-client","version":"3.1.0--canary.134.924b11e784307d463503e4e2b41f79eceb6b4396.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"924b11e784307d463503e4e2b41f79eceb6b4396","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.0--canary.134.924b11e784307d463503e4e2b41f79eceb6b4396.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-ljR6xCvoyMcxgptxhqEsUDlnTrABdmQlSvw/BnYpWadz/z5FCsb29xiP2xdZkebZ8NoWDOrHmBe/thok555Zjw==","shasum":"4dbab248ee9f07b76523675d69933fe9707e7e46","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.0--canary.134.924b11e784307d463503e4e2b41f79eceb6b4396.0.tgz","fileCount":68,"unpackedSize":996514,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgv1oACRA9TVsSAnZWagAA6VIP/2RzeY9wqSNhxtfss184\npQhfLaX6z2zFwAKgRqJCwkjvHYmfrKd+KkP/1kmMypj8V77YbdqZIWnaKVjr\n9Fet0SqVWgM2XmxM2dLIYdRuzZykCBEt2tdfghyZ9yhqB2uTO0FJ3JKNL1Yu\nWjWf57MfcC7PQ7mPoYNtpDnhSMujtw9Jj904uXTc37ED012A8RXaoz/Kcorr\ni2rFL3RB2URnx3i19+NeQxyRHQkpNjZh5osERVnilWqkPqFTSRA32kNK09oF\nWnC+NVpO+F0ihU8KMlXA0ubf8kSic7+7Pe3uip1iYFjG7XZGI2p4M1QOOY3Y\nqmnSlx5PGPhEb+9Fq1srQ3u2QCI8VG/exvJPzU5RFERcE+pPtWmcOPwhq2gS\nbDNAFNuf2yiq6t4CCncavrbcmwAClNC7KoubiFtZRGfP7UYUyVPyXZ1CP6hH\n5OXdTm0neHBTUFUUDw6qxp6HFBnNcA+2stadpxqoxl0bNm3QFj9E9c3+1/bj\noZzo1VjX+ayOzB+RLFtIO4iJn65Br0ZoluwhxKpA0Xl9UGSMeeXoSnBruYX4\nDSRctdVhw531lnHhqmKQSQXv1C7F/Q9H3v76fHLjIGUg8FJko/18kU3NG/gN\nHMKKkHU8nu5vNCAUq3xFN7si+Nqk0YSURJEOAZ9Jnhz55qJVshVr7nvZI+hu\n0pJA\r\n=OhPc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDwu6NIAFxqlUuBGUbqSAYWoLVlkqUHXh9yaXD0P6VwmAiA9y7B9mj/diguCOixVwq3HPqnbg3A27nIVAa4kbxydgw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.0--canary.134.924b11e784307d463503e4e2b41f79eceb6b4396.0_1623153151914_0.5708054334987489"},"_hasShrinkwrap":false},"3.0.1":{"name":"@sberdevices/assistant-client","version":"3.0.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"bbb16cf7f7a394c054e2efbf8a9262fb14d9e949","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.0.1","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-BCp9rNM180qYDrB+ZHvXqAh3Z5CTquKzGP6I6dLZc9///lZ/ARqjvEkNFawEHaCshw57HGPiL6gn+7mMG0qQTQ==","shasum":"4fc89cb1fd5f477d0abf4b3297aecef53fb1fb1b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.0.1.tgz","fileCount":68,"unpackedSize":995430,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgv2bQCRA9TVsSAnZWagAA9eAP/2cxQOna7OmjfKPViegF\nF6LjUgaDvrH/4psRNohGz1szeOfLxs1ojlbZ5y/7oI4cS63oOqKiYF+OvZdH\nl4aZGuw93SDfDGXJ9Y3yYlrIwQJcO7Sxy8TPN/6EhPtZcHo4Cywgq70+LDvS\ndwCpb1NlnhrkxSBSLSApMIQhFCrPTa5UZ1AUbZgZSSsM1nhpwK9wCLbp00/u\niyUdFGShPoQHD22dEvAz8n9XMFXRLF3Xgf5lc1e4nkojgVR7rd7HKpyzY7bs\nwqjsbREw/shVWq+qt6KpSd7ypr88EKtLm+bNElNBWU5Oc6nBWBR2DvwNS1I2\nPCQJW3JeJBFRnNLM4TF01H78IuLigP1t5k/9gMhVmqKwJgr2172Y05m+znlM\niO8W6bhgDClNNCcAz8D/eOsb1Gs8C52ssYSSqtyKa0AecSH2XkV5NujfJ9Id\nS8C2v97juaCOmI2dsFoAcMWYQlH4OQ5Cz9dLKTWw4ANYtPX4icfgJ2UJ7/zo\nKPNRDNt2Sl1EQCZ3EfDREYoqBrZ5kaZE7Zotuif2gqLqybXG30jan5S/CNJU\nil9DpcGBv8G6b3cv4fRdexp7SRNqiUWI7+dWY0MRpddIx8wzv9MOmj6/uQ5g\nW3MBJrjT16DJDNtEsfIaBwi/pPvgv7hpztT9SmXpOoIZkcxpOMQdq7oRfAxl\nI3MS\r\n=iRew\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC0teOqIxb83SC4NM1z8N3761gVGl3v4hMngo5Ps764GAIgJZ2WoaSZxglwgRQQ6Uf9GlZFDB+dz/QAmZ+aaseKh/Q="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.0.1_1623156431819_0.38464583211445635"},"_hasShrinkwrap":false},"3.1.0":{"name":"@sberdevices/assistant-client","version":"3.1.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c4fad434a787c7e606003a93deec9e8c1d13f1dc","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-GoVGmfKYt2C8IZloytSqJ8GDizbgC8JvWnQBVITUea36X+fTf1nXPvo1aIBqry7VeqhDCbYX2SKI4olGL70oeg==","shasum":"8dbed0c96cf88367d03cb5fd203fbfb00a4d63c4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.0.tgz","fileCount":68,"unpackedSize":996805,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgwHOgCRA9TVsSAnZWagAA/C8P+QG/A/DqL4GNqYjYnuyq\ntvlZgK98oQ3FREGraUPGALDVNeC3IX0GzycCDpER5YjZOSedlhul8fan/Ha4\nJAcUGF7+/n631xtdW+96AvEZo57MtaPzm4p+RJxV3DFQyVWq/jVLjJpI+Lzt\n4TP/vKQ5xERe8rh1kPgwp1UcBtUuPkWPVLoq3tNaKTAMniBbnENfBoFWedec\n3NFARwttNRaAouw2r4sGrxdIKvHssDcFyV8mCo0qLfrrG+fTVGsL6wQ4P5i0\nhOQ+lcLINNuTIcAnLu4LRfjxzVbKGPQKMqb2tlmtQ/RxO8WG+XNQA19KRX6t\nYQgag2IU5StipST4QMrwQMftd+YWLhJVwiQgARt/esUWoUAxe4vqNju78pwp\nHzi89MMQZrsiFtbokqJAFxWYnQe1g4nrmysdyUgCweFtdsHlsmXxpTSSpovw\nLL8qz+OEGSGbgmPsnfuzcckvDfb/54pnahOoEENFTQUTLhRglptdmgng5530\nz4qEB9wMugM0DL2TG1NWLynvjpAekCL2ZSnSbVAqg6g8ySTZ3mn9mKcCX8Jy\nTSdCcx8w9EJ01ISir8EGa0kRSbmReh8GaUUDkDzyFSXGnlBLSXXhf95RPAON\nvsTuu6z35mzstW2MIjJGSD4zqtgHG2B6xnl24mqcK5VvOD1/EK2Qz1e5+GPP\nKWj4\r\n=p8+/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpfguhHDg16Rj4oe93fDhcPPyXERfMgAtMm3bzznS2WwIgCoA/0jrGft11HQo8XRW6dRSu9eUidYZ4LbbqgvYDn2c="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.0_1623225248699_0.03499338319178458"},"_hasShrinkwrap":false},"3.1.1--canary.135.ea4d71e2036f9323fb24bfdabcdf2807ffd9bb25.0":{"name":"@sberdevices/assistant-client","version":"3.1.1--canary.135.ea4d71e2036f9323fb24bfdabcdf2807ffd9bb25.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ea4d71e2036f9323fb24bfdabcdf2807ffd9bb25","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.1--canary.135.ea4d71e2036f9323fb24bfdabcdf2807ffd9bb25.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-6NzDmxyRwriQE9thNAd/4ANgzXXo5J26XikyzIIOhtGrwp3R8GpaX5TMZkwLswI9zxdqPd1/1lAzIzTa8Xp1Zg==","shasum":"4e22f32505697be92c95550b9ebbeb2a1c9fa163","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.1--canary.135.ea4d71e2036f9323fb24bfdabcdf2807ffd9bb25.0.tgz","fileCount":68,"unpackedSize":997005,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgwIDVCRA9TVsSAnZWagAAucQP/RVOhWQMunH1/ek7oYTi\nWDdJwfurzTNWAAxr3Z11oSkDKCNhAmSlXouuOeYJ1PJDCknElGCa0/snyGXi\n9mEAlFQ96d6NqvdSaqifN7mB76HJlW5+90ayyHiwhDG18of9I63iMOI31Uk0\nAOwUFwhD7i2I966eKojciXQI6j+2V5NSjAS5tMvsGqBCH9IKRMwm2GdQIh7F\nZ0RHfF58aBRG3m/hbIPEXaKn/XiN1jSO1IQ7xiTwW4Xvz41p4UbqRyFClmLa\nMRv5tw/gvuEeQm93fvoOG/d8n0v8Q5dA4vK6n3asKhS39DDHNKyumFsmEqBT\n0kyTDPxC4hxIrt+2uQzSRKF++jF2UP1Z2hE9JhKf+dn41cSQTCym6sY4/5s/\nfMqDgw35mzWF+nvtIV0h6SUTSK+ICNaparXemUIrY2vnm91A0+BVeooKQWI0\nm6FvTIKMvilzwel28vPBnDBfPxyRSf6ak3DsFXU6mfxgGtED6hN6JfNFh9U6\nl0Juz3HKmsCeLd8pqMDunT4r1b6jjW+W8aFX/DG/2xruh/gTgW57xW9TGdFs\nKgZIiTT59/DD7xZ1y21k+UIAxnitrmghfp3hIS09CuZl3kv6jh0IcUWPTSme\nm2+kGvjB18EzNUpjCL6EfgnhmbhzVZJFECdouZCBU/WR+4N2w1uzVhL1bPwZ\n6wtu\r\n=jzhR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDskMqo6g8/RdTNfQ3Hrf/BfaYuPkJhK7ATEnhN8cjVnQIhALFhp7fWPnFm6Ajd2whsKS8Ntk6w1TbWMXQIdZAp/6Ul"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.1--canary.135.ea4d71e2036f9323fb24bfdabcdf2807ffd9bb25.0_1623228628881_0.44716215712609486"},"_hasShrinkwrap":false},"3.1.1":{"name":"@sberdevices/assistant-client","version":"3.1.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"65942c3ee525d80d3469068fba1206b150caffa9","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.1","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-aJ7ki6t/IWn14xgPMFx1dMFOj18IoZt8WzttdFIcNnx+eH1OwsgI99+CZbwKCimyen2wVQ49kBzIwOmchh5EWw==","shasum":"12f53240d45269db97b3a570e597f55d5f545f97","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.1.tgz","fileCount":68,"unpackedSize":997109,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgwIZDCRA9TVsSAnZWagAA+ucP/2g9krhMXWuS2sGBb/m/\np5EZXMcGqOb9WW88sb/EQmQoH28nkbRKxiOlrY0FKY8rULIIrBB8JO7I3pK6\n5EjDSDkTZWY5wzqn/o8Pk26nkjO8r6vrAJwcG1yGlaGrykmMLDyW2Y7L640p\n1uUNk1NdGssa4RcG9IVvLTlArr978h3nwlz0pA9PXl84MWqyxXfbfffeudm2\nt8c6KbCw+e0WIgZ6Ns5SEEcH69V7ZE+4d8RAIzk0qPbb72TGnTucLcnXS3tP\noE9ifi8riqf/t17aFNuoKR7BqEe90nzSg10RBBnFyQyHrDZQqdBeVoirTR0n\npwNDNwhyocLH7PcrXB5yoSCwATLInjbfBbhBFBhjYaYZ4q2n9SKl6Oe0nGBK\nDz+K6ZO2YhFs+4laQPYuCesez8zPs+Nv3KDAxVSVmqvVKSL2qI/Z+q4qEiY6\ngEUfOIpIMlUJWWHzSOE+NSpaChPxVM2nCFnx0ivizl6gDo4iNR+bCF4guF7j\ngL22Rj29NVMd4GzRmP/cx6F2l2CmlTaN9Ae/vxlMBukUQN+xdgHJnsC1zsXU\nPoHB1DZnjiiT2VtqmXK8KXU+svfODrPVR98U5JbssGeq/Ws4yr9R1kHltBl5\ni3LPepPdQ10eu5DpSFYX22L3QwFPTBqZzinvvleV4pPwaGcDnoft7QqvFMJs\nVl9b\r\n=Z4zL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEJIRdYZMLJw6brC9+LF08x+PAk5HklkdDS+7cVhXvoGAiBeS49t8U/+r9v8YOQM/w1kdxBjh1jvlDe5V9clqJaIsg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.1_1623230019391_0.5816607472680355"},"_hasShrinkwrap":false},"3.1.2--canary.137.317a672ea55ca3ae4aaa66547ba736c55cd938fb.0":{"name":"@sberdevices/assistant-client","version":"3.1.2--canary.137.317a672ea55ca3ae4aaa66547ba736c55cd938fb.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"317a672ea55ca3ae4aaa66547ba736c55cd938fb","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.2--canary.137.317a672ea55ca3ae4aaa66547ba736c55cd938fb.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-EXbM54p7Hkhn1MnN/HDiWXxMyT5Mn6KnxgsMd5xDXoa7d3pcnGbiNX8PDiI9F1EBkiJEPaT4/ecJTybLafoeHw==","shasum":"bf807d6074f013a2154a7f875143e1781fffa558","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.2--canary.137.317a672ea55ca3ae4aaa66547ba736c55cd938fb.0.tgz","fileCount":68,"unpackedSize":997642,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgyF1dCRA9TVsSAnZWagAAVjkP/0JecB6QqmA1vFrNsYel\nOOFIvptO3bfqrPJTl5C2vXrc109ED8n9r9a1GF1XgTJ+8bBxyVFWr4VKBWJv\nIyohvbPrAgGfvxXoh6R1dpSDPfXA7rbaSRiLJ0+Seb2l6kjPRWpY4Ejp4Bky\nx2YF3Wxaml5LMFpmdMx3tqkPqMQMsocVaPzmUcpg6SxcMSvO9CDgIxDEKKQ1\nbv4qJmB+lvKfMI0Qfym9XwDbkw0ZW/9aesKnoeoy78C2dG/9Z1mRF5ZOEvsF\nMvEn1cs+2hYxwta/1oqP0iUJy7OJotSo8Y/5ptfVCMn5NJKwpW3EkO8n93Fl\n6+5WSPE3mAoOAatY2q7LAJVHAGBu+zXehC4zXIXbri4XhpqYxfwl5Kqluv+1\nC3nbophNg7kuzeEolEi0VsAhwpLji0n6d2wR6tGE08Tgj0/yeWHZ4L4f40gT\nZGIit1+AwkjU4CwLrWixJGs1VTv2uC4wlhg42qe14W/tmIsUn6eWwlXJig03\ndkeGwZ8JRB0V3Yljn+Yjz4dVdiLxWHArllQxPblydekzGTMIf6Dn/yo9nUFt\nCVFX8gd46tHfd5xvFK55rbZS7DEAePfqWgaRDCTc4FjU7/xUNWnC3T9pBF2W\n4sWStABLLRGEvH5MTN5TudD1ZZXLQzH6Hb1S4/2fD5dMrS6ARs2nWhLukVLB\nr4Oh\r\n=gXVX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAUWLcM5q/T5vM/zSzsYdtGS88phGC97v0KLMu+ROX6QAiAqLqwxMKlilgqC/PXsD2Fn4yBKzN3oCzKhQL8SsylWqg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.2--canary.137.317a672ea55ca3ae4aaa66547ba736c55cd938fb.0_1623743837724_0.8760562209038301"},"_hasShrinkwrap":false},"3.1.2--canary.137.d0dbff8b7a5ad6a66ef66c4568f90fee0cbf8786.0":{"name":"@sberdevices/assistant-client","version":"3.1.2--canary.137.d0dbff8b7a5ad6a66ef66c4568f90fee0cbf8786.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d0dbff8b7a5ad6a66ef66c4568f90fee0cbf8786","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.2--canary.137.d0dbff8b7a5ad6a66ef66c4568f90fee0cbf8786.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-RP7Nvc9P72xSj+tTz0v7QsSBcw0G6CmW4iUzz4qz+Vqave0MlDV/eXCA5VpuM5C6LM+heCxjcyNRUalLrSfKWQ==","shasum":"b96ddfb2f7f4275d0bf25a53e8c539863d1b02a5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.2--canary.137.d0dbff8b7a5ad6a66ef66c4568f90fee0cbf8786.0.tgz","fileCount":66,"unpackedSize":995213,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgyZVKCRA9TVsSAnZWagAAXDAP/35bTaVNuuUZqIEQL8we\nLirEUZ5P2/ttU73AQGdPt9ySm2ZbGzCRqn+mvVa2FK8ugwySpPx8++kzHhBi\nxkd6uJbxzmh2vRAIV6VFro54LQ4YtqWZOK3oCXWJrKPvbZZbV8kEib7duHH5\nrpsh2/qxbFQTQy6tlH9/qbcKWvKJUoGrZkS4lVVVErGxaAOJwV68ChgiTD/s\nErqw6CQ5c+BUw+150CpU7wx+PB+9hZZllKzaj1JO1e6/9jXzCdaVvYo0+K3o\nxt0tiN2RKuC22sezju+G4EhDQj54aDIWyHcFMJZR2Xt21+X4n+X8MVjEgjwe\nwfp6V1ao+9kNZ0LecT79LyswAVfznwDSXl2IhOKvLCT9zD/w+MKGxm6oXVOB\na9yQ1KINrteo1f3t0l4km03dzI4EmvdzXCn8JigxRQkMpLA7akB6Ft92nNgb\n/xgpVXO16uakhY7Kao21DIkSvKILKw5RdXoduizVnNy/iTPYQHno7Tf4DDBr\nptok4GfiCZTOZwtFjzZ9eIC4oM5AR2KDShcbE/mNbGJPqO6oVKmzqUb9uOSx\npJP2hpq/1wQ0zIoU8Fh3sVl4CEI/C/Sc16UOg+KWdykWqe3TmdQIrqgn086/\nDoJ5q/wqf61FpC/NclpWGZTU71R3mHsFfV1IfkJz14fyeaUqCsfh3U7HFVTp\nYfFY\r\n=PnTQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDXeJETA/EVmbxlfWrG9hRB4AYuUoulF5OBc75GiUg4VwIhAM3eN5/jhu3N3mLCZ+ykU4/jyg0oljGOYDupcYr1pDib"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.2--canary.137.d0dbff8b7a5ad6a66ef66c4568f90fee0cbf8786.0_1623823690765_0.9965754985249522"},"_hasShrinkwrap":false},"3.1.2":{"name":"@sberdevices/assistant-client","version":"3.1.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5a0d502c95ecfbabadef11e543f9921f0dbd800f","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.2","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-50oFQp52po8uxt2NTvJbucHiABxdCWgKdQnQCe+5GyJM5KPdN7dPfvtJldJkz9YIt34sSRitMuhfLdXpNrpmTA==","shasum":"93478e7964b6c49c5f68c4c226c3cffa32550cf7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.2.tgz","fileCount":66,"unpackedSize":995516,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgydT5CRA9TVsSAnZWagAAJ2sP/3XEwcE8Pm6ngXiEAKp+\nnvVzlr24CJ/qZR7Z2V716EQW9hRLN91vlSuwPPeH22X3p6QP1uziEKZv5SkB\nh1FvbKoFR7Yex3Nwaeds1/GbWHndCvyNRTu4B4Ztz79xn3U5O92Wl5NZgLR3\n99y8kV+jAnBqpt6vMoLza4xWTX0L9vRcPv2aKlHm0npY2xXTYURxCpJXkLOe\nbQxbkI2uGWrBa2Mi9J+WGH+OGfKN5oqDVuWs4l+bSV5mEDdgZ2vqSmmZcUWr\n3xFhNyH89cf1zATcvSlVfPl0JBEdiQPrKpucNa9YKjSX/yfPLWhF0VEjijP6\nLEYjyaWjIZJWELAUGVC5+IIrFqHSLSltagFgNSY9AM2ocxcZVEnmb0WimV7k\nDlhcjvPvSCa9aK6Q0my4B5Ac/9518wi2Xmz4ObfHjMlYizM3KtWW6Hjs61Tk\nxK8Z+ia/3H7WkIgao0wQlaTlJ/7jW53opKEh7GTcZes4BX6+vuEIWioZuS8P\nIZObQW1CSGsL04w2wGVcILIdi/5st7hiF6My4fL6h4T9JGPBwLOCla31f7o4\nwNf9EHAXba0+HNh2BbiJT8qa0icPtWJRi+8wUe0u9T3zONMa81/12DqxHRQv\nEyvQUjhSOz7+jihxUEfkUL/UPxnamC2ehOXVDl5/IIl8DhQnJ6dWhTJqncFp\nyAPU\r\n=lAyf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBQtmqSD9hxpl3J2K0gAfAaVdsiaKbq/mlWMZ0XpiXL+AiEAsMOc/Y7HaIQ2xLn+jqWYqi6to9q7uBDNJramEju97vU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.2_1623839993504_0.5184501685247"},"_hasShrinkwrap":false},"3.1.3--canary.138.aad571b58e8495f76799803193098be65d462e43.0":{"name":"@sberdevices/assistant-client","version":"3.1.3--canary.138.aad571b58e8495f76799803193098be65d462e43.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"aad571b58e8495f76799803193098be65d462e43","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.3--canary.138.aad571b58e8495f76799803193098be65d462e43.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-uqOAXa57G89H4hXCBe9PuGFjmj60l2reBS97TK5kZVD/ba3qrbpPz5QPsc3OBdqt7L+lQxFSFAVG/0Ble305Xg==","shasum":"9f8153fbf4480ba1eadf06f810b224a97c2b48db","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.3--canary.138.aad571b58e8495f76799803193098be65d462e43.0.tgz","fileCount":66,"unpackedSize":995735,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgyf33CRA9TVsSAnZWagAA06sP/1Tvva+r5wK3GnhmTrmp\nGAnYSVJcRHGoYiGy/Jxjqq+NaXSA9O5cds/2hPgY+YFvGQptwtGZNOPyt7/p\ncHUx2G0+HC5iou/HfTVQWJXuroac4a99HFTpAVoqtpE5sNWrRZsIrkB52cjn\nM2O4jcoNgpC29BosyPiBfvN9LJXuRuv2E8uE8adDnj53X5EMnC20wnKJuppG\nE0+SY15p/Sthe1F0/oWN1OwldgyWrasYu7KLwBY/8P6KM31iZDJnndrBddHd\nUnHib+dVMd0bIShdZ1RdWeXrCN2wOp7aHjyF0PKxh/1dDZPdqysPgZ4k+HLw\nDMwnqQI6V3nZtkoRegadF6woJyndjcp7CWICj2q+M0owHnXBihR5sStgys4P\nlwCxloYQA6LR/JTVDLNYd7b6o1qBLZsYKibiGpPqY+ym56EFGSPMQJSoHmIU\n0WKj+OY0WlD7EMkMsaGR2ttwCN8VcOmhLOvKwJZbSqTVZLfLrCLlOD6qOrFQ\n/VPWDAviA0PG07UOBYcdKLFE/h/Shpk1O4IS++gUvGeC8k7lmqoNREiSBGzX\n2PSJTc/y8KJffo8xSij3i30O7Ep8r2gtET2HFFUVOXLY4PMUmeSH62Si0rtI\nc+GH4OQW90VArPLNNuLS2boVoX+RdEyKw/eaftyo391OEmk1EPLsOPBtMHXn\nw+OY\r\n=CfPV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICfdFmtKFAst2r6Cy15F3bOD9wKzd9nWQbp3/VfzilDeAiA8YcSXe8nJeJ7oE9WsGb4rngaJ2y/UgajaAZbxFbE0Tg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.3--canary.138.aad571b58e8495f76799803193098be65d462e43.0_1623850487145_0.30641567222968247"},"_hasShrinkwrap":false},"3.1.3--canary.139.070743011deb5c9600ad3f868be4fd194bb2db87.0":{"name":"@sberdevices/assistant-client","version":"3.1.3--canary.139.070743011deb5c9600ad3f868be4fd194bb2db87.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"070743011deb5c9600ad3f868be4fd194bb2db87","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.3--canary.139.070743011deb5c9600ad3f868be4fd194bb2db87.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-9Th2UB+WNqaY+I6tMnThUsHEf+bh9wVhdwnz5IYAr67J9sK1fTjBfzHhQShUcX44g/TqscuoqXoiGUCv0yNNUQ==","shasum":"129c577ea93f7cb0c5c9b7cf1562a5156352d394","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.3--canary.139.070743011deb5c9600ad3f868be4fd194bb2db87.0.tgz","fileCount":66,"unpackedSize":995734,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgy0otCRA9TVsSAnZWagAA3lIQAKNzLCGIu83ttIXlQ/rB\nc3Fmo7v6kXChw0t77WrHIFwo/JKqj0uDQZXMtWWqU+0trdsKlqifUhWzhlKj\nCO5Vg+opLFQ6eNW5fwNssIhrQcrqBqNDcEcSPxyR0sctPnf7fT4pjoN8lv7Q\nYom3LXTgSc0bn2fJAk+vHe6wpWutTWc4zx59a2cMzHQUEWhlmXtKbhPDoP5A\n2q4tpmSWkpfD2ilUNpGcPihyw++5lMEZRlxh/2Tig1vyaPMCL8avOSt5a0do\nABVw7Wig2YJrs/suEtrZeCLXnrgZC2EZ6tRlSReZxN+YouXTLjh7F3rSwQh0\nClCTC0FY4RH5eKvmkBUKfv8StWu/ePkcQCQhnhQ+sZ7RzRCh1GuhKVQZ/xaV\n47R0Zer+DduvWnZeUgllRtS5MRh67KEnShl4oQFqMofb6CPSkwrPqTgW+Bh5\nWhT1oa4LutjyNVX5kJJpTVjcq2FM2ZKvMZby3sqWt9sOJ+V7BOe4e6a6Yztb\n1y3KzvPfRDSmmTbqdDg8x/oBtauayzKxsF2NTDONJDQ/BrU+3XeBCA/hDj8P\njfnqyx2x0OJbIBLlK/eqknUQJ2KPyqsQSfCoNQzvxHR4C6vOF2pzEjtzzU/2\nqNzbycmEE7OTKNpArnlZMLhtSy8xphAj/VbbQAjOGgWymkLmsiApkmXdP9QY\n57c6\r\n=RicM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEquVM6W449Js8fviFB28C6rKca++jBq/6v/XqJXQvjEAiA6ObIhPQY7Jc4mQN0FMgA36CORsiWXwL3hMf7/g053oQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.3--canary.139.070743011deb5c9600ad3f868be4fd194bb2db87.0_1623935533109_0.9498814509065281"},"_hasShrinkwrap":false},"3.1.3":{"name":"@sberdevices/assistant-client","version":"3.1.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7396f1bccc6eea6d81705f04c70aa5f80b999c37","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.3","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-IqH1kypJYOZIcZcac7BSSZZapOs1vtmY8q3aj317V6DKa1+nKZxn049oV5B5FSUK4YXFZczNenPmXiIOaKxQug==","shasum":"665168c3d6143ccebad1168777376469f804360e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.3.tgz","fileCount":66,"unpackedSize":996101,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgy1faCRA9TVsSAnZWagAAiP0P/369DXnHhrbU3owuEnBx\n7a6qk6ovUIoniwsxbZIV1YDcN7FD5P07pPIjbJjJ2Umlh4t+tr6WYx+o9Pfm\nhE9YxdShH8sMAp7saHS4CcpELUfWLsx/HKf4Wwhv/02FDKePkHe/Gyxp3ftx\n2pKglqsnH+rQbbpzYvBPE3VuX42u7WLXUjRnsvg+qPIg6wJ+C1tsR0/szPa6\nxWLJe6nMOTcT5QZxPfsnENhUYlog1bj+IY74+WJTlbbIrp61F8Z2iPlxG4TK\nKwzC6TyFC3KIfa5eRfxGwBJQQteL+WLNhBM7ePMjj43XVESgCx7mVa3jYMHH\nBpDF5RttxQ9w+DGmqk36UwK7sltumThUK+Ct4k2xSnuFKeVwre9ilFlXE7mx\nQpK6/rHjYvmu6q8rHocOm42mjjuVK/WQg/lpDD1GsB1nd8GjLQJiaNQiOooy\nTAk39aQr+7lxhH/Ghyhho6ez4lvGBDD7UT0IQi7JfQyMQqW2LdF5n/jEnUDB\n91FKI0uaHT3sn45lLmAsYgJ2f/up1oKeFnyzn3ly6EzbV3PlMhWLeI5VqdYc\ntT2LwIYnLdO1FHEdaLC48+pmOnBan1+PtJrB7bsR+Lb9dW4/+ezusVnckfW0\netOU66Iegy3Ot62+0kQBxeYYk8EoZdn1YR9mV+qZjv1ZxB7nqifyKMJxeo6i\nRTub\r\n=83Nf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG6k0xNtzsIkPOTAh4gER7kdq9KPlZVWuJg6Yzr11W51AiEA17vDKBwEqCjVbSm7IMQ+JoP4uljrRehPZvGMyugyHMY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.3_1623939034060_0.6164560125094212"},"_hasShrinkwrap":false},"3.1.4--canary.140.7c04943b0511ffa0804719742e50aefd25329b70.0":{"name":"@sberdevices/assistant-client","version":"3.1.4--canary.140.7c04943b0511ffa0804719742e50aefd25329b70.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7c04943b0511ffa0804719742e50aefd25329b70","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.4--canary.140.7c04943b0511ffa0804719742e50aefd25329b70.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Sjk4Evtkp9QAglTsWvzHnx/LWtuz6zGyYUj7WyWNEt6kJh7hPBJj5K6jEBmBnfla48k7QVHV13wlt2dfz5yFrg==","shasum":"d40743a0f098f4b84d97248e667c3bae2042d87e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.4--canary.140.7c04943b0511ffa0804719742e50aefd25329b70.0.tgz","fileCount":66,"unpackedSize":996602,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgzEyrCRA9TVsSAnZWagAA1cwP/0HOdUdrMSIiV/iy6Pz6\nEpxglr9TryAU9KJ9H/bx45xTZwXPI42+bjDOdNW47g2fbdmqxUXoj9izYU85\nouWBkFfzEGG6OAhgyID9hALuXA2stuUKXJwtP/BherCj0xWJLu4Ee9a9HvL9\nXtiYwEIrWJvLJf80HaGkIA0BzAAgUCq3ltPqoVgFlTED8odJYqse7KG0EycF\nnt+kzirty5qeDR1b5xyrAojINbcE4nYV3JA0faWMOnhvscX9AGSZ07Gwxn3C\nMCKsWa4u+F1R1NeW6XBpahvVIGsuLQeAH+0Eo/Stu7H3XrzBtOPdzug3IEYZ\n6Ap5pO2FMaWpIQicj+Rbn7/Op5TDlFgCzLD1Q5gO2c2MtauMOA1WyQSXGemN\nGj00DJwrJQ8vB+pnKRexauLWwkhZ8qy3tkYI/WkokINTJtn3x2qPfjL9QIbw\n942YZ9jDcJy1GaNNYZP/npsk0mkkEbIfMrNZJ1u4f5zNIompQC/eNok+T+Ca\n8WbJq1iAvcVzEfl2zRbyIMk+7pr5zwBikpQtEMOCHFWZYRiVb8YuAZTIsNAz\nVcJU921pDdLtIREqkswrviY7vudjaJZpD1SHydG3YEuTQ0p0wMWZiE5FaTIO\nyJ8jmmEcgb8MD9G8yss+PzBS2bNjX1+ZVYABfTgtig39FXZHuyV8Zsklfu/Q\nt9XQ\r\n=V5U7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCbGOtB2PIXW1zS9ByyHJG00zvmEonijLBc1ody1k0JAgIgVnmRi3GG+8BjYr6UBdQqUsopEOxYuwVh+NgjWQv6zyY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.4--canary.140.7c04943b0511ffa0804719742e50aefd25329b70.0_1624001707064_0.9878321266130079"},"_hasShrinkwrap":false},"3.1.4--canary.140.7281c0c6b16e21f2bd751f8d129b2639b547d3ec.0":{"name":"@sberdevices/assistant-client","version":"3.1.4--canary.140.7281c0c6b16e21f2bd751f8d129b2639b547d3ec.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7281c0c6b16e21f2bd751f8d129b2639b547d3ec","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.4--canary.140.7281c0c6b16e21f2bd751f8d129b2639b547d3ec.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-7BDgM7zTxvnzI6chaU8edOJUbU4CeV+a9W8ZjinEHQY6Cr8ZMhl+CTYA8snksT5E+Padm1Tp4v+6XMSLMBZ/bg==","shasum":"cf49126595aceb6dae0464861a6696e7955aa84e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.4--canary.140.7281c0c6b16e21f2bd751f8d129b2639b547d3ec.0.tgz","fileCount":66,"unpackedSize":996602,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgzHUrCRA9TVsSAnZWagAAc+EQAITw8DYkOLoeLpUpKcK9\nL+7OAKdRbCs8+iOJUQSIuEnTSE5q/ESLQ6jqzxa+7q3iaM0dLZ0itXxvL2HQ\nWAWzeph0GQgJxP7UJJqCany/e+B/MXqN4lu9u078Xx+9KNsBFb86rVo0sVtG\nAgWvc6fOLrj/tzG93/4snqK3unJjSmewEZVldTtcwOF3xroJHJ/VAbdI+XAP\nu+OA5d2yithxUgMHPqmI5E9rNCgkoZ/+mC3i9bK2GI3zJKOttoNMARM/4Ty6\nKAZ+W03UN3e1JniwK7C05s7Z1VlUAapTMV3k5athYptg+FlMU2+jzNIRxgF3\nfroYorWAhB2V5G1L0n6KVryVFcCvYhMxp9qzFqBaszyfBjU3CPfvsJN4XZx7\nYm+xrOYshEFQl2D6l8BouZhcEvboqalI9d0s0m1FTQ4IGp2UeD4FoX4FpmKx\nhsdEHqPRPpdEYL7Kx0RMjGqd3eYYT1JizFOkI/5KhoSSx/zWiVTbPeuFp8IJ\nS2ldwRNzca2JKjC/v9dklK3Y4duDZXr8U34niTRqq0dpBC2ZQuaUGvOELn83\nQAk21+LyBLzvRbsv+pKSbpg9T1Xj2kBOkb47vNhaQBKUdZQr7ea0FnfG0hZ4\nSMSY85ZEChElRGeH3bHOCKROxnCN1wVlMsAwhW4DWIOhyhTVakS0cBpNahtU\nYBXO\r\n=gdok\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICO9AZuayzgTA8+u7d2oTCH7p5JQFxfdpkaKwFEYcKBwAiBKYewJDJuw68qYD0gdsjLIu76q3RXXxeuf9tc8ZWttEw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.4--canary.140.7281c0c6b16e21f2bd751f8d129b2639b547d3ec.0_1624012074955_0.14107430889447836"},"_hasShrinkwrap":false},"3.1.4--canary.141.5c48c6172ddba2e94443dcf52658fddc63ec6227.0":{"name":"@sberdevices/assistant-client","version":"3.1.4--canary.141.5c48c6172ddba2e94443dcf52658fddc63ec6227.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5c48c6172ddba2e94443dcf52658fddc63ec6227","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.4--canary.141.5c48c6172ddba2e94443dcf52658fddc63ec6227.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-f9Hf/t1WRHV4GdS9ZNrLreHvD+/2QYq3JuiBlMwWcT8cioPrWK+HGwitBPFIuCGIIVRvWf+k6NM3qB2SirLP+A==","shasum":"49be6b0206835c7ed1713ae7ffa72183f282936f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.4--canary.141.5c48c6172ddba2e94443dcf52658fddc63ec6227.0.tgz","fileCount":66,"unpackedSize":996657,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0LBvCRA9TVsSAnZWagAAXlUP/j34EbMLP6YJ1X+9xZxc\nvfPrxcwdepBLQciXm/vp13p8q9k877/Ekh5Szuct9yRmaUdjdOWwvQS+PL8k\ni4J2skm+7LDnOQzrqgfrVFtwdiI3sVBZhce7z4rS57hlWsV/HKbSFp7YGTJ1\nF3RTXZ0ynBytmL/4TT1ZipvgFcvJ2UHHbs0BqcJkqp6kr50SP6sPXRXnP6dT\nhAsfjhH9RryEyIBWf7x3UxwSeMTj5hrJ6TXub1MyZMc40e6q2b1kb+nzlcv7\n9DQtz9EDpx03SIwH32SjnS/oc0zCR/a3gk+SPoCZf4erjYFGTzPmB4Pl0WJF\nmiUq2FCwfWxIg3m6IaQDuCFnFLgTSaK55506BwGpgBaWfmq1eMp+kHkvhj72\nDanXqkvfxCh7J+gvI4vYRQs/1DlezvgXcIW2eozeO3g8INU2A8nbZmjuN65J\nUOUZzRSRdyCZKVOsqZGSjb0cw2K3aOTkFUGvsk9Ufqq63OUyfLyvhot1z2zG\nTYCGWFuu00TLcgFtQ7Qy23nTnRLoqFhgQi3vnUvFN/CN1lUeUCPIKOasJN91\nJe7dpnqqgxXbn8oKTXOv43L5pk/6L4sfcKfYofjW4nepNM8uTbG9GRumudjp\nB8VD+Y+Bfy0daQ1BYVT0SGGVxAeRIhuK1mjop8wr3t7pw/siB3mBjZyddk4g\nHQJZ\r\n=nZn4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGxv+ocoGJVx1z+MMLi+D04JJVguP2QZq+Y1YQ6p5lP0AiEAsBGD/oA4NzxeaTvYW26omp1hhWvUEuVQURlf4vQ7ADk="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.4--canary.141.5c48c6172ddba2e94443dcf52658fddc63ec6227.0_1624289387910_0.5486727940841138"},"_hasShrinkwrap":false},"3.2.0--canary.142.a8dcf88c19e645dfaf8fc1544c5b3d158a90bd6f.0":{"name":"@sberdevices/assistant-client","version":"3.2.0--canary.142.a8dcf88c19e645dfaf8fc1544c5b3d158a90bd6f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a8dcf88c19e645dfaf8fc1544c5b3d158a90bd6f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.0--canary.142.a8dcf88c19e645dfaf8fc1544c5b3d158a90bd6f.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-ZqqMb7MwuWuGQHX/PIM4+3MvHQ5r31LmcJ/160e619bMynDI0Qx9CaKu0+oo5/z3OCQz9JS4dIhFjo6qwH8ZBw==","shasum":"f098017d1117871cbbd87b804ad613fd670c2bd9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.0--canary.142.a8dcf88c19e645dfaf8fc1544c5b3d158a90bd6f.0.tgz","fileCount":66,"unpackedSize":996994,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0ZLrCRA9TVsSAnZWagAAelIP/RpoIyH/GjEOpHIPjiwX\nki+TJotXDY70CxIvu5k9dNZ+GnGMxw/ppLX5/VdWjHkmmTGauNgwJ93ZJd4G\nr8LwLmad1vk8oGDpTt2//VDdAcMEwitqbbWED81Ferv5Nh4N03S8krA1NuHt\n/AwQQ1WcVofe7y+h140GFTaQurOnk/bRftRaOK13WEy/V54q8euG9iEAkaCO\n2rCO2q1ZtxSj/AZgh21CkRWLF6g28ut/ds/N4pUYp2PPrZmvZ9XWYoFAuneE\nB4P5hiGjPA3wxSKwU5TO5XBJqf2WnAvUKw+VfPJX4dmPgYzoBOtJSB0u5tbJ\n2r3btlgttbgOw6YDwhYYm+b3v/4CCvMj1+vuY7+xgpd61/mhcLPK/HXOpmho\nJS9t+QqeiIXOmd5oCE5GlJvWMAC87YCg8Tcf5QPB6Mv6heHkvuhj7KXrovTF\nJa+4WvGOII30FJN42a8t7ICrJZXMvZKcK39OcMCMb6zphL75jppB5ntFZeAZ\nE+CjUdk5umV7bPVHp/FNBMhODspgfyMaGOTLPfVL6+5FMFcgKjsie/RHLCCl\nA4GYeRee3RE6hxK/ad/irSwvW3E91rOK5SZPPPr4zwllW1z+qtzovoK+saM6\nvslxuDyIWKKOaNdkya1xyuot6nptwzGuGtZSDvgGM9WKGqTzIVbLvN3nVQH2\nmWOq\r\n=0EC0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj1Csjd8kqGUX1JVS5jIuVov96fHf6dn7zZrqB8PA8ZQIhAL+XLPdgIClViPUR/dS5jJ528F6N0PDufgW2pcAbkX0G"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.0--canary.142.a8dcf88c19e645dfaf8fc1544c5b3d158a90bd6f.0_1624347371142_0.5170372614504781"},"_hasShrinkwrap":false},"3.1.4--canary.141.c52369d38226c90a17fc3782b603b79ff53abc37.0":{"name":"@sberdevices/assistant-client","version":"3.1.4--canary.141.c52369d38226c90a17fc3782b603b79ff53abc37.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c52369d38226c90a17fc3782b603b79ff53abc37","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.1.4--canary.141.c52369d38226c90a17fc3782b603b79ff53abc37.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-1kRXFWnjfGS7o4VFjMtjeenrqAqxayuZH3Om3OY8b2WouwRj4jGwYoaTnC91Db6ukEcWiq++zXrskGCQtEnidQ==","shasum":"bb705cfc603a662e88d6ca4505663ef80e1bbefd","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.1.4--canary.141.c52369d38226c90a17fc3782b603b79ff53abc37.0.tgz","fileCount":66,"unpackedSize":997661,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0d6ZCRA9TVsSAnZWagAA6OwP/jVtf5e4AItYAqVJEND6\n51TcoxZokVzhTG6ikeSWi3SCrR23G5knYrsJoU1AH7CXhW4EnvJfj5MxLy/q\nIcIZAZkGDwCQyOrfV8te4lOZyi8W2bowt+sn8z4S+6kDrKssee4lLrCX3jPi\ntVOvZJqlS+CjiB8I6J4Tsnd66/5IrpMBwUyweecRj5Xg5Ag9oMx+d03vorVW\nxwtgChP7JD3BDBerqhiCHZAtE2xOK9DqDb5pH3z59RDovwdcYAo1chEheLY5\nTOFpTIs+YJURZsM6ux430R0ImFpmT8kk7SQKE+t8RcWtVwVZaP91OLokvvcU\ng4M1ybfSMTywTi3j0x6LzrtHtSMX7s66kpOuk911HKKAgVgy3bJTceSwCawU\nNrWwvGacBTAljE/1798uhIr+UgdfbOzTRvuYhrI0ZhtYwOgOvRsZvcvYBlmd\nyI5QNWnMwRL0OA8B+2zliMTxPCUhjebi2Mj4UJlOj9srkkC8wp9iz8/oS8oz\nBAj/SF3d0AYfKtA071VfGKK0wFhpO5rY/ggVD8+pHjRazCeIM/3XQw+o/bOP\nT0BfMuTtkbx/JRIgawCzVM0AFwP4/E1eUN51Ad7kIfcmI5C0enHKGzPcfMXU\nWG+eKjmLYmlvhWPa8X2LxhofI2I7p0jFfvo7p9Ip+PrDDRRZo5arce5IASPt\nBh8E\r\n=KXHf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCnIYrFBT21/MNjJNaXNnyk7R1ackYx42UwJYx/Y67PLgIhAOsnjvkzamCCgYjUSA3nW48eLiIeC8I0RySVhsoyBbRa"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.1.4--canary.141.c52369d38226c90a17fc3782b603b79ff53abc37.0_1624366744693_0.4014872574335526"},"_hasShrinkwrap":false},"3.2.0--canary.142.7fc1eeb2d8fb25c988634172b82eec30d2599369.0":{"name":"@sberdevices/assistant-client","version":"3.2.0--canary.142.7fc1eeb2d8fb25c988634172b82eec30d2599369.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7fc1eeb2d8fb25c988634172b82eec30d2599369","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.0--canary.142.7fc1eeb2d8fb25c988634172b82eec30d2599369.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-iMyXmvxHmGgX5dICxqguNCYb0VSg4t+GsPUjTxC1IGhI4HYjylLz//x8gQuJZimrRbye93gUXgtRgy0rVkbOpQ==","shasum":"88a8842999a5217e5435d5fa90f08e294066e53b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.0--canary.142.7fc1eeb2d8fb25c988634172b82eec30d2599369.0.tgz","fileCount":66,"unpackedSize":997156,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0ex9CRA9TVsSAnZWagAAVSYP/2EkCwVRVzw861BoRJvP\n4gkKFhsCEV5QyQBy0YICswMdNwvxWk3S7ZKGfxopzpd4ZtCsUxQI250wHWlJ\nzL4B29fe1WWtea9ZFz1wJPIlP/Ci8mVBH4RK7Q4qVaZJvFP8GnWmXEMSXtWY\n/WO9f6o31bevyR8peQRu5YvSA4X5IAWkNjyxqjLhHLgNaP2Yxc6+Ty2aTnFk\nepvOJC7DJQgECgUpA6zApIQdmIAxnqs1hpOFm1ZaReTf0DSNkB8NpUn2SQbB\nUW0cQZG+WMhbvDue9htNNYoHpB0ytRZAFBsxn3f62pb+/pJpN1XSMlFV/EQu\nqjpx184Lku+blDTjI2zerR82GPniWZIRYJirLvCT6nmi4a7kL42dklj9qgcJ\nsSwkSd0/lAG0k6mmXWkEeFj93SpqnlgDGE7nlWLBPZ53ehBw8WpyLiC8rfp5\nW+Xkq6KbjIidI6fw5ja/o5Iq0gMg8LamPbqMCKhtkXuwZDDCPRJ2R8L8bE75\nCdSG5yDHTbTngnRiydVNC+7TSMnBRsCCnYCJfdIxtXa7MOeXDziPxnOAkomA\niy3sgfS6A1BOj0IyCgGarooG445K4U1xnBpUTl3+mGhHqZyXsMwUP8/LYrIj\na4mCSmwmRS/GGw8YR0ZK+iXeeQI42qqltSXZ6hA+9k1adZeylXdqZZF5nbuY\ntHwS\r\n=e2Ei\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG0adw6IPjXzmJ1yiTjEUaa2RBQMHLHl2EXTglGGRSGtAiArlMxMkIlB6PvQnri472X7MkLRluaXxyhoRbgPhJlIMw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.0--canary.142.7fc1eeb2d8fb25c988634172b82eec30d2599369.0_1624370301007_0.9011310375025035"},"_hasShrinkwrap":false},"3.2.0--canary.141.8f240a5806408156ff3dd95962684bf8345786ab.0":{"name":"@sberdevices/assistant-client","version":"3.2.0--canary.141.8f240a5806408156ff3dd95962684bf8345786ab.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8f240a5806408156ff3dd95962684bf8345786ab","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.0--canary.141.8f240a5806408156ff3dd95962684bf8345786ab.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-SvJToQ2AkQZpoSidzVe9UAFZkBYvkNNxbaqNXVnA2lHF0RPbSHgX9q1nXCq2bbaWk+itf95HImRA/YtjQZLM0A==","shasum":"6929d1f214d7d0b899f98e8b99ca885738a42eaa","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.0--canary.141.8f240a5806408156ff3dd95962684bf8345786ab.0.tgz","fileCount":66,"unpackedSize":999502,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0gHeCRA9TVsSAnZWagAAKgQP/1DRd8ClQjQvJgxfgjL0\nyUG2AngG5IBaMk4BFnjWLUNmGT2lieKjo5eU4UsVCv+zejfRiT32QMP+hf3Y\naZlaS5gDTax4VhgmDSjVhdpX7NQm6ApqRMq3c9FRlEGjl8GIycdSDdbSx4hB\nfla1j8F4WrkOoNm7g0hhj6bw1K5kadr7WNtGcsA9rXSnFmGWQuN+6wRrb270\nC3VECLucnxYXCA7dpMiAIZxG+cLXp9Oyy51ExPLn/xlk2x49PVUfeyxrDKJ3\nE3mej0fzbXIt4N7J//mODLwmtd+tMkiwNpY6YpA70dGP967fWyMQUj4DOMRs\nQlK0cM+BK83p0VP1hxoVZuZdx0pblIV4jM8qb9J+WKLtCtWG+ICQ0dPrK7+k\n7y8MvAfy0NxrrPEFI5Et42EU2OVAGFSaMJbhVKADmVOaimpcQ9/JFimoLGCH\nbLiSk8Juq7HTieMhiMw0F3wDdFQ6TtDsUtELr2BI3Xal3fmfNZqXwDEl6N12\niln81fBAM8+Dv9UTwtPy9l03diGcragWXc4X38SLjL4AxHuM7ghSwP7pvT0+\nEwjjYZFle1A7/ijdf06f7l/PtNpmw0jKHa/MDRJb9aawhmHmsRhHRkt8sr5S\nQ11Z+FpWTld9gCA4t6PAs7YfnRrtv1OjK2Fvn8G7U5wGNDZz8DXFhJmNU1GZ\nMM5E\r\n=AFrn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDf+MBlr1Tjg1dVtai/Qsf8kbZHgic4aGUtBiNr+71X1AIhANka75JQskpHeXQdgdVsstwRMnQ2s6ccnd9Wai/SRHQU"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.0--canary.141.8f240a5806408156ff3dd95962684bf8345786ab.0_1624375773852_0.4584064812719266"},"_hasShrinkwrap":false},"3.2.0--canary.142.0b6a469909055cf4ebb25e80a11f93b5d335bea7.0":{"name":"@sberdevices/assistant-client","version":"3.2.0--canary.142.0b6a469909055cf4ebb25e80a11f93b5d335bea7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0b6a469909055cf4ebb25e80a11f93b5d335bea7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.0--canary.142.0b6a469909055cf4ebb25e80a11f93b5d335bea7.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-evPc598beu5gtu5uGy4n1UZ8PUWpS238nWNnGSmjI7bahOt40i8oNxt+QY0HnVaGAXkKhVasBzFW0Kyt/SPOIg==","shasum":"3938977a0675d7c2538c8d5096781f1c5c7727ac","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.0--canary.142.0b6a469909055cf4ebb25e80a11f93b5d335bea7.0.tgz","fileCount":66,"unpackedSize":996318,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0uOSCRA9TVsSAnZWagAAeTwP/isxsiiO5AgnmP3BxfgA\ndOtBIA52EdKtA1oFXPGl+yynOSXduLBw2yrju1xtDxvN0b6NUr9Y3eeV2h5u\ngLXwv/JydKUZOzGGznUvJrb6E92T4eb2wvbJjDntxbzdqlBj4+Ra3ih5xeU/\nk1CHARZmwR34a4HoNhl8znFpEyayJ1NjulfoibusDKwIyMPWyiT5O3tZZN3+\nrNmMjlD+CXZCVl3ds1fr9JPt0rGzEaLAw2weCe+Vhx9mkisNfxjkHLuC4FV3\nHqhL3b1VGDu+/f/HZorrDc0uQ8YyZQyoMr6UW4jGHZwK2vfaZI/j4IBDWNYi\ncUQozLiCZU0+hppw400uqfShxLQ9dGiFJjPDREa7etqhyJxddgT04eY+PRIM\noknJma7UTeOJJnFi5bqzqxHRP/pM8nELlEmoibz3VGrxOKeYbPPncSjRBngW\nqbPGyyL3y8mKZuffdNCSLCUkLKBx5kJ0jyrGN62X1zONX/adwpmxQ1h3R57e\n/jirr7AKpYIrcL7nyAjyk6yaLRyO6q6Y+OhABimrY/RqKUOWBRylt3qJLyeO\nfBdQYu1RLnq+mExXVjn0v0JA05OXyVgHLBltHsE/0sgM5zRKxePxAmGd4ECI\nECftvdvubsP6+pDSaYIUEzwgdOpamrnNY/eRxGLaY92+VyEdd4XcpPjVVujo\nbKO9\r\n=mx6h\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG+/yDnbcYeom8ABXdBBqfcghliv3YB7cUy1fCcE93RAAiEAuW9VkYr0BF+J4FWnW6Zed+OLOOaDvr44zrj1guNttKk="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.0--canary.142.0b6a469909055cf4ebb25e80a11f93b5d335bea7.0_1624433554044_0.08591262038105807"},"_hasShrinkwrap":false},"3.2.0--canary.141.0bfa854d335eb9cb2305c9311bdcc1bfcc43357d.0":{"name":"@sberdevices/assistant-client","version":"3.2.0--canary.141.0bfa854d335eb9cb2305c9311bdcc1bfcc43357d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0bfa854d335eb9cb2305c9311bdcc1bfcc43357d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.0--canary.141.0bfa854d335eb9cb2305c9311bdcc1bfcc43357d.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-2VKF/3WjCEQNAMtvnMZjWEaebD1d8Me7aEZp6wX/IiqDOaBcXg5EBAdFRVhneq+18A8WImmc2VfsXljAtGy+QA==","shasum":"2a1f3bfda0b2a8047159dca7a8f6392beabb0368","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.0--canary.141.0bfa854d335eb9cb2305c9311bdcc1bfcc43357d.0.tgz","fileCount":66,"unpackedSize":999907,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0u4ICRA9TVsSAnZWagAA6ZUP/21rQhPAqFkOmHMqXwbU\nsqMQVE7Opa460NzotTZm0Yt9+P5+nvWpUmVNIc9PqtFWpiB28Db+9XX5K/ca\n2d4kw8YCXrESTMJChLTuRFmaYCVVC0lR1dRxJfP65ObrrcGWRpxnerz5f4ww\nGZbpy0IGeQsthGEseTeFTIXsu6yCqF4dBWCdC10voM2dv0JbqB4TfHzoWel0\nnsncqOHJT1PKEmw1dzkZdNnsXVhUyLzBLt7AlzJ6jZKFlcHofJsi3iMljy0E\n4p+QfBzvnpuZUtEMjmnHfhK6/fLqv3y7hGuyYpLheyQP5RN5or8dL0kGQ8Yu\nc1Yp1Jl2MIUtowHY5Enqz6WajCMQgsILG1UpqrGTKt0au4Z9xmx4TJWfNiMW\nTBE4cGLirHH9mZQ6lSkbZOR0Xco0AHyNOwlbbDTFSotzFX3fYAnAY1tFwYOm\nUtiI4uoM+9Awa6fC8cX/dNwCqF2UgPZedMti6n9g5yEeOalIoIjwpe5X+Voe\nlQekm77QhYwqtD7wi9eec7Mv76vwRCcky71c+rZKVUsWVG7ZRGTlx/DIqyA7\nuUdoGpKkuEexefMueh5L3NdElbgE7vMm4BXzjV+9py+NBnerEuXBTorIlume\nkk86uStnox4y3MEQ8qobvFtc3O14GCN+lV+Kj0zxartxs8WgVu2AjfozNmr0\n5Ct3\r\n=hYok\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHskJrogFBGTeJUzA2JCYwynVDtR05mRDQdIpOmSIoglAiAjrhwzXnyFebGioYtWa8QbNyaVQE0jAcnCTLox3guC/Q=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.0--canary.141.0bfa854d335eb9cb2305c9311bdcc1bfcc43357d.0_1624436232334_0.558474979993308"},"_hasShrinkwrap":false},"3.2.0":{"name":"@sberdevices/assistant-client","version":"3.2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"136257cd28d46fa0c636bb69ecb44c616bc76bef","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-dUON1DZxMpYquoXPnOUywf7eHrXjDE7+znvbfBkCM4ZnIZdGOZnN4l2h8dJYP82KcItZPCrnMQw5VfRZvCj7jg==","shasum":"0b8368e0795f1b8d83470f5995b4dbb9a9206307","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.0.tgz","fileCount":66,"unpackedSize":996681,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0vvPCRA9TVsSAnZWagAAsukP/03Umj6PmVa4tDd1wupD\nGl9aKLnlRknddqaG0i5Ww6/GmL2e6MwRWzXzxM+/AfmiIOccc1dtZmnVzq4r\n8czH4IsCOFmpnTWa2OV2Y2C17VBxDJHogLIgGEsqYdi5GGdzVdw+syOpC7rr\nOJKh2jUT0j/BJsdUyIgbD2PEYsNOOTGhZL51Xn26Z7MvxfXmjMMqemH1cWIK\nT67KHkDS3EcRQ5VOSJDsT2UjIdyOvWxW6hOvqbs37f/orXxQVCA9jg5qUlZJ\nV85N60droQFR8f0e1NZueN47LOH9z2FI3nI9gbAfPI4UnynbL56hmKTmEnQN\n53ikKCWgMgMbhJqrbBwrHyugcVt+yeH9V8V4flmnuq8k8w0eIc8HIJH2u9J9\nFci8zJoykbtVDCV0t9HinGwmeUrMBhpnEKt+8gT/iY2z9JuIzrwpj6qNJE3g\nh8gj4s/nUrfo/F3acl4jC7jJz3k9lFrQU9MqZ/rbOFKQF7pf28O3e94vH0am\nxbaAGngnBHCZOzjl9XOcz4tYn0qxSoaoHdyTzXlaAFIgPbwplw/AXht5+ozn\n01nR6smqmCNy2wJFU93AHv4cgzQD9ysge6Puz8EMRDRe5l6zgSJHFdAhSyHD\n0XeS017gd4ZWitqg6Hy6QmdDNMi06ypsqWdZnIT4apoiL7k558B4MWrMhJ5X\nl1P+\r\n=BVPo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFUtG177KSO1R909mCJzaiqnoKqYZVE+ceamXuDjvjr+AiBZ2M5X796mJ2iScidn0jN1LLVOjSiUUuADNmgPT9dGPg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.0_1624439759054_0.584457596691194"},"_hasShrinkwrap":false},"3.2.1--canary.141.35b2c78ea672c7b6b9f7a1a33c77e92a1a360ca4.0":{"name":"@sberdevices/assistant-client","version":"3.2.1--canary.141.35b2c78ea672c7b6b9f7a1a33c77e92a1a360ca4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"35b2c78ea672c7b6b9f7a1a33c77e92a1a360ca4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.1--canary.141.35b2c78ea672c7b6b9f7a1a33c77e92a1a360ca4.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-tOS6Chu1c8z9o2U/VCPnPaEEH9q0KdmBu8hM7bRLlvTGtDMoC63Qken+OrZciJQT4HZgWqg8Baqygdmb8RhMFQ==","shasum":"aee0934640c8f266bf9978cc67805dac4bb0a0dc","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.1--canary.141.35b2c78ea672c7b6b9f7a1a33c77e92a1a360ca4.0.tgz","fileCount":66,"unpackedSize":999616,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0xmOCRA9TVsSAnZWagAAcrQP+gJVuy1C1usgoB+NaJNa\nGSPride5Gt+vKbPjZwzJGJj+l1pRVD21GjFKi6Yp5b7nJDSx0yy4DHT1iYBj\nI0nEPTdeQ1IxpYwlvYPBbYQ/nNvruwBcndx84n64CrB/3gb9xKipSeqoND5e\nWcgH4hgoDPabAR+C+QggS794KUlQ2BzXKuYcgbdfgZqirJyh9VpGDXZ3IJL0\nVASMSvbSPam4Oc8Y8j2klIqfgdZ08acpPIEpG9OM3gmHQV4MdWnQihsmSuuf\nM7r21rQlPG26YoeqY9+jRz3bvkSy2rE5rb/tbFzoD5fPrJ8zjojoCTPgS0pa\ny2XUoM+8igR1wzD7nOWtcaP8jrQvIq6wDwr8kqq//EFPZryr0W8qPXcrYhbf\nxqtLd+OTzfK1i9EIVwprEN4swk5s0Xg/mqKIzggFH/WJm9hFaZGO+4hk4bi2\nBOSMr8QabECokkOV2SFZvUgVGwVmbXXmR7S1wQ9JQF1MruXX/ued8zPkLCk0\n4RN1LVyzCpKwaM0lX9glgTP0Q8heGkkWD8iv9wj7W3usyJVZvzzCVePT/4Jt\nFQXqQ2eGV2utharRtZRqdxbqmlPQ0ZgaQKf4VvU8ZQDtqtx8YINrERbu9882\njc2wDH86s95AAY3OEGauRUv52MMmQVyhaa6AcmNMW8PkF12kn8ijEldUlkdv\nQgdX\r\n=kJcv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFWJFKjwW9f2yT7gupRGlo16hLh/y4h2uu2marfvbXPMAiEAxZ65XwLZI2LOSUgdjUqbBwaj/sjQPQfpuE7sD0SvFJI="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.1--canary.141.35b2c78ea672c7b6b9f7a1a33c77e92a1a360ca4.0_1624447373947_0.795507473716176"},"_hasShrinkwrap":false},"3.2.1--canary.141.8b64ad708755ce8779179a5fa7353d6a92abfa2d.0":{"name":"@sberdevices/assistant-client","version":"3.2.1--canary.141.8b64ad708755ce8779179a5fa7353d6a92abfa2d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8b64ad708755ce8779179a5fa7353d6a92abfa2d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.1--canary.141.8b64ad708755ce8779179a5fa7353d6a92abfa2d.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-hxwbqg7p5Q4JssJ7eskqo00EeSrWWNP1it7DpBJWFgG6JUC3ho/yexix1aSTel5I7eSBRxqRXT6Gvr+Ln0V0iA==","shasum":"8ea07beb83e4b26277890bf4d03c0d89316a24de","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.1--canary.141.8b64ad708755ce8779179a5fa7353d6a92abfa2d.0.tgz","fileCount":66,"unpackedSize":999275,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0zyRCRA9TVsSAnZWagAAw9MP/04SYYL2A/W7OpDz5kiy\nSAY5bqYWGCH1y1F78xeEhCYWKNhXuEIAeAtBW3eJF9VZ0ruTCb46nYSs+8wS\nnb/e7faM57S9FKRVGRE/OENxg7VaDpwuXNpWuGFP64CSfzL0cwMAXVkFJyOs\nMYo9f6jarDnX/JMawwr0pU021dsZrJB2HTcI8PAun0kL85fAYWTHNjWGNqFH\nknpau1ZN/FyFdfAR5VRl7JmrXxoXX5uwPR2tU6n7E71mcKQ75s7vJmMy4EOi\nyAR0VwDHdEygDVmlFPRmJ0LjAEO+rkthaT84b4XjUHRgpqYEIS1nerkEvuKT\nCPa/GNkxAKq788NmiExOIYhQZCKcRRYfhTj3QW28CSlhzLGJEOcrjRAywvm3\nqwQ6u/eySADf6iM3BIl1EqEBmGslAKJqh/S9f+q76neL3+tHXIJ8K7Ksiamd\nroNlK+mz75vXoHLlmeTW1NnfoYSa03ADNomH/+twbc5cZZ/LdBU5f4ekk/ne\nZawf4CyqFaxQ4jVwkWJPDUPSWB+YPAni6n2GOU5dRQR2iqgGmJc1blOT96Aw\nC5KV/LuHUx91lEF2sHK0FAJ1MmxXHobHTC5/UDi7tWOJmStpXQZ+ra7bL+6e\n6jVZG9BoGooKlrOAhnWVV94yy2yCY77DoMX3jXOYqjqEonsbaWfnaNRiK733\nZNaV\r\n=/jQr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCet+to2iObrCr5ATKpH2qU7IBPU5j1Xn4Oj7eksP4LoAIhAI+Kj1QpWOivRV/s8hUe+GRj9MMp9TckFN7oDkTe7SyZ"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.1--canary.141.8b64ad708755ce8779179a5fa7353d6a92abfa2d.0_1624456337180_0.4637633871564588"},"_hasShrinkwrap":false},"3.2.1--canary.141.18b6054c94bed98ec2758f0446a60e3b47ad3c1c.0":{"name":"@sberdevices/assistant-client","version":"3.2.1--canary.141.18b6054c94bed98ec2758f0446a60e3b47ad3c1c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"18b6054c94bed98ec2758f0446a60e3b47ad3c1c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.1--canary.141.18b6054c94bed98ec2758f0446a60e3b47ad3c1c.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-mLx1mqANFAnHQldobMf9BnY85Jhjfwol25GoLVACIRU4K4qfJq4AdsPJzbCgkEm0l5QMJ6Bw6IbiJLsKb3fwGA==","shasum":"ea3204c18dd038a6a093d8c7a63f16779598714f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.1--canary.141.18b6054c94bed98ec2758f0446a60e3b47ad3c1c.0.tgz","fileCount":66,"unpackedSize":999276,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0z0JCRA9TVsSAnZWagAAlwMP/2DbM9QJSZaaGuQ8A0D8\n8Fds0gBwZ/klAv+vnHzoaIHZjnxDrkySfx3GJVT/PEczGKlnyISq6YiHQZFH\n8zt+MKhk3raY0229w3N9BwJ42kNd5qzyTpMNaOFi5kQvNIMX0HKa+hO+/6aS\n2QwvqUaSKRtdQMbCF9AFZLVHO2wxh+tqiDvfFJG1A3e5ey2E6nGO/DY2unOb\nyEswtYQjepvy3FiA5rEWrGy5tcdwoIXU8xoiEyvWw2GfpEbXZRlGY5ThUurg\n0JxvnxCmKpGSifRizhn3diL3u4NcPMPK0Rs1AmZFgN6NEes0oQfJ/LmZI6ci\n6NoxxQVuD4413EvuZ2wqMhRGMhSGEHrgRUXKg9XXJy/RoFCJECUolQeZO822\n4GPb53gW3G5cUHQNV/0dInvc4MEZEGy3erCkLsLaKGBWCR7h+sXsxqdmoia0\nr1zKqWyfZZ+kvZ4F1XoSO7nHRE47irfVqIfozaCFtIHra/huWYg3yOWOxiHf\nnrlJCc4KF2bRpporcIK41C4uhD6UbFVtLm+5N2FShCvKqDr7WtfXF0SmU3i3\nu6rc3seT8w8N2Ckdd5z4yVaFbH9K0FvjZjZdwFEJm4moG7htNjskUGXuTiUH\nIkznJ/rKJtIM4bM+gbglAzCcd37s77uKhtG3gpwj7Yy0Q++6H0gZTfRByyLK\njB4a\r\n=30Cu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICBR6tFE+yW7rJnotfUw8fqEANoQxem9vMpkPk6g3S9WAiBA00hYXaPlEGEndwB2ouQcsSt0mO+z2hL/beJhHbVxTA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.1--canary.141.18b6054c94bed98ec2758f0446a60e3b47ad3c1c.0_1624456457502_0.40039943094693076"},"_hasShrinkwrap":false},"3.2.1--canary.141.f7e643a47ac4546d75bc73138de272dc7128f603.0":{"name":"@sberdevices/assistant-client","version":"3.2.1--canary.141.f7e643a47ac4546d75bc73138de272dc7128f603.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f7e643a47ac4546d75bc73138de272dc7128f603","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.1--canary.141.f7e643a47ac4546d75bc73138de272dc7128f603.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Db7oyIrXMtNQlDJI0ujyeMUFfO0mrY8rjBpFFXbd8TeQ5NOfrIrgu4YDK+8JmxdScP4aEn++TQNA5QTHUjGhlw==","shasum":"cf4694b2bdf45562b7fa5b27b22581b5ee003d12","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.1--canary.141.f7e643a47ac4546d75bc73138de272dc7128f603.0.tgz","fileCount":66,"unpackedSize":999106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg00AYCRA9TVsSAnZWagAAPXsP/A+WvQAx1euSBErf8xGw\nkLcplj2kiN20s74x6f4Vv50Ud1KCTX7NV3P+p0xx2BSrBxSJs7i1cqOIpS44\n9gqUa8NU8IXd36HbgOuQDqdtUU7C64kBRbp8KJqWLWU/X4pE0LHnstxKQXY5\nWXG84bqS/6vfy1whKG//hDZc4LLCooV/Z5GDHLVYBTrGsC1ngwnYVkJt1JH+\nLAgXiLy2FW4HcDQBh+t18KGVpZqMRV6zaeICj+/8qpWVsEqXDiqR7BO5wi+B\n632uuE1GtMHeygYwr2G898XdZ4bL1i6C4IktmQiRT16ISbKAs2Xq5xuRJ7BH\nW5S9LQ0KSYtcoJaF7C89bH9Gv8nsrWzyVGvFZSQfectKTUxE9UjfdZ0Ukjnj\nqi8HCzDBA5CoW/kWfzKjFpXL5HT/EoDHueQRm8tu+enQDRdUGHyIPFDKN9b5\ncIOtTOg6dnfXIxkB5O82FUWbxclypzC5/uVLQio3jk/ZNX/ky/yZ4RAi3Eje\nF9197e/dQfcBAMyBJzViFOwPjFAKzaqLC2z3QSZE5W49FN8t0jeZ4Oi20vN5\nIt0qvuX5T/cYGtmy3kl8yJ6mg+z2VEKUaWgJBPbh8DPwpA60Wkoe90uu6B3H\nlGGvd3iLUDIIXeq+45C4r+f6TtMLtv+3uL+J1jHjVuLKxFBqb0QWsA8eXQ8P\nbm4s\r\n=qC9D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCjnwcRpzvyd5AqS2rXFxMrncymsu1HCGYFlkUc1KqjWAIgMtJOC/QtRATcOVteWm464gV66H7tlT0HCJj1xpd2Dwc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.1--canary.141.f7e643a47ac4546d75bc73138de272dc7128f603.0_1624457240031_0.16339806825137604"},"_hasShrinkwrap":false},"3.2.1--canary.141.a4a52f70508462777c549db3db4c32a674a44900.0":{"name":"@sberdevices/assistant-client","version":"3.2.1--canary.141.a4a52f70508462777c549db3db4c32a674a44900.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a4a52f70508462777c549db3db4c32a674a44900","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.1--canary.141.a4a52f70508462777c549db3db4c32a674a44900.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-I3NON+X7eaGOg+J+aPPupaMgJSC2hlpUAaPmqaD0slsdZIULu6+IJ5+QyOjdKczRIfrBHrNIH54qwREoajhBNA==","shasum":"0dc47d6319db357457c387693273a575e97eee34","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.1--canary.141.a4a52f70508462777c549db3db4c32a674a44900.0.tgz","fileCount":66,"unpackedSize":999259,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg00wuCRA9TVsSAnZWagAAy9UP/2jePWeypBur2W+AeuVL\nhlptNzL1lB3ByaHWcsPN8X5um1i9ZS3RJtU6RlY68OofGC+6HzdqB6FeAl4T\nAup72y8FN9UCHPdpzTiq0uO+XyAv0LCl8T2djy9hfr4yjOtBtRNWIdsFTXOu\nDImydnAFZJAgcethAZ0bFxvnI7hFjwdGfE2tTvqmaCJgorXgc1hhQUSkA66Q\na+IxksJCA6HUpp7vWQeSJuAICmqsdi8VVoUPRlMQjiipkfZymndkSI+uiBXg\nGCQfT3RG1yoCT/VRB3p/Ruu9+T+MerXo53msbtBNWh/LGDl/DghzHyYs64y+\nWH2edtrwt8DrpBupgTkoYpjLqxsWKCkhcNg4i+nOksiQbqC5pzegKvz/Q394\nczPZu6C9Kq8qcES53sFrd5Y4R2JazHxsWQ36zCxJdajlNzYfagp6q86Wdnu3\nuuNAwfqPKJOR6TYUsJfmnTjyl9esQDipXztSfEnIIXHU3Vo/jVZyclEnpZNK\nO3hEX/sxh3KxnSv6NaoQoY+YnQRUziP3U/iQHECy+dVBLQQW4gukPwMOvDg1\nX/S7GFJfetGcvOaSaWw5+KvxbUPtVJGkrLRgS2JnH+2e616S6rwHZ5s4BULk\nQGfSP1GSuO95GveRzLD4Uv8MUHErLqW9ZcW2I6MmBFcsBoAp2OBthRGAv8Zb\n7xvJ\r\n=/F3H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIER/j/E/IFyzGHJFmqZOLK0XVdrSPKcG0DWItl9roZAzAiEA+rC/vCIsOfgD1rDcOQ+IeznBMx/SEDXwIBymrxWFhmQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.1--canary.141.a4a52f70508462777c549db3db4c32a674a44900.0_1624460334051_0.7078683014191871"},"_hasShrinkwrap":false},"3.2.1":{"name":"@sberdevices/assistant-client","version":"3.2.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4994bfaa5694455ea0947edd8ad4bfb0a956704e","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.1","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-AqjdvFLfTTnpEqyc6NEc3pj6SJ8qXqV5aoDBrePEmXRKWUf9Gbcjr7QV74hBBqY1uvWKuaMsjlSe0hRTwiztig==","shasum":"063b254d0cda28f9b6b1af8110d476180aa37f70","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.1.tgz","fileCount":66,"unpackedSize":999432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg1FfZCRA9TVsSAnZWagAAYuwP/1GzYO1OiCEJZ5fPW0PG\nymUu1YLPexBNpkFOW5IWi7Y1w81TaGw82i4F7NzyppRzkxD457iHmMQ/tvMG\nInTGbN1tAqgJtENT5pDz5TszMf+El1Mm7emuH/Z3FgQRd6NXME5buZc6qOa1\niDCiDaorxjwabhNhFhqR6OVwiSJHjMlYoL83DsTgG2RPjPaId8eUG64cYul4\nWXubg9BqnJEoC0NumwA8nEzv3L5uhKBfd9XL0yzuL9lEAy/ArEjaIS3NrTLj\n2457ZafkWref5IzWg//wztgcyjdVWcMqEHPfZ0Ww+ytaSAi+9YpoFd/YPhYf\nIqww0aA31uyYIxct42O6KOZ2MJXjVHKyu+x63M3EXWDR5iGF9pDR81+dqkP4\ns7wdzPSy6w9e3zU8eNPv6CQEbUMQVTXd/gJFVjtvZm/9zKfZLgi034JQAbSC\nC8aQXJR5Bk3Gu4YNZKAe5P9AtBP67X82s2s0ZuVsdZ65VbbZRjwKkcJ0hT7G\nAJAlaMvpC9eC6g9ExX4OK0kVv5/3DDW4e7q3P9IelXRsStyC55ZFAE2+mY5d\nbKTUXNmHMpqHq/JIY3RC2wOSDN7YD37yDkeGokhcg9sAoFf74vVj3wNCxHZv\n7o7yDUYWxI12DblIycJvLhOh7KYbg6LnsNfxSXGQpQaYZF6rR2D+3fIyVdVD\n5PtW\r\n=aK3h\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID13P6IJ3VIXA4jEBFSq3LgK3DSg69k8vjODIdTW4KMRAiA9hqkT8G7V5DZDYSbYTI/tVQEAf3dVGCdzQn8q1FMZ1g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.1_1624528856729_0.24047670690065703"},"_hasShrinkwrap":false},"3.2.2--canary.143.da5ebe43d4912beae154c00674da7c009f9a3345.0":{"name":"@sberdevices/assistant-client","version":"3.2.2--canary.143.da5ebe43d4912beae154c00674da7c009f9a3345.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"da5ebe43d4912beae154c00674da7c009f9a3345","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.2--canary.143.da5ebe43d4912beae154c00674da7c009f9a3345.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-jvBgALTY5TJi+QpxwWBE+gPndb0firWmrIHJXUmY7rqYUV1L4CbUAeXHz6H8v7upQ2wdKRYQc+M9nMjdH/t2JQ==","shasum":"f7fb6620f1017e9474e8ee26bc284084d727147b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.2--canary.143.da5ebe43d4912beae154c00674da7c009f9a3345.0.tgz","fileCount":66,"unpackedSize":999611,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg2thvCRA9TVsSAnZWagAAJyYQAJ0umpDD5SEv/IdApFGg\nFYgrWAQ0B4SLuDFW5vR2/uXGFuTh+H4Z4xYgUdKUnjvHnW0w7bFa5tbP86sh\n7I+jslF6YAiNEuzSWZ3o5xXmHU208F/mb5Pzb+H5nfOAVcOArROQ152DF+5f\nPIMFG264C+Ln5JIv5+aTiqEn73RV7jaaVOipKLKLw3OJPbNeVbXthYFUgLOB\niBpJ4zhTkZnIpXAeW+Qnzp6NWtQxXHcXB/hcg+nzQS9oPdHjBiCdbBRKfJ2e\ngqOSWfMNXOGCe6bkMh9sQSRYpqtVD12VxI5aqfQji5NZCgkIs5FK3LBlRAxy\n/jfOb5mLaLNLvq46qLgi0nSIVJXEjj0n86pMovywJz8sjH1bIP6K03+oZmgv\nxFadGtC21Zbb/ZC3A+lsWcNcBD6+2c24vAcKiJPznIajFL/gQsiNT+BYe4Oz\ngbyed3/G7Nct+Cu58442q+UKZL3NXEK0n48LzkxmUvHsOdtzXRryO5yi7KLe\naXCxm7XdOg1kGY+2woY2A/EgJoJEl2dy3dpJ/iEk99WBOWnxECPcjyECiTp9\n56PTAfUlYwxvdx7HIoqFYp57+IWTX3YiKMRjpco9UDqU0Ff0SDsQ2YQ1qqgp\njsJ0+OmCCR/waRvlMHWriT4hl2tbbW+eknMZbHu13QKdGZOI1VRULdrbcU5a\nlV2J\r\n=aSm0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICZ+IkKjfop5WwfTDuCeLaYvMb68FfI49PxPp+/BmCjcAiEA81FkgH5Z8UQyQ1P2MBcwnvYrVlu1XsnySxG2Uo6+gqM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.2--canary.143.da5ebe43d4912beae154c00674da7c009f9a3345.0_1624954990799_0.9572701987111427"},"_hasShrinkwrap":false},"3.2.2":{"name":"@sberdevices/assistant-client","version":"3.2.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b8f634322389e40248d5a33c803e4855f816a461","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.2","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-PAA8WLEn8CYXu8/ax8HW1/5oli/UtIg7oeE5s9SJ4uxvWKmIONKkgQLlRSvNFHBu6meQruSpF9oIKsYRP0xChg==","shasum":"8580d33741a6794a4ebe847e69e0c5cf74951ae8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.2.tgz","fileCount":66,"unpackedSize":999751,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg2vRkCRA9TVsSAnZWagAARxkP/A4t9wrwaZxt5MBZ2wXH\nJBs79vWlqlMsrt40wKryxCpetv5GeDQ6nTAmh83E5P5UbAxMq55s11FKgDZA\nhaI2RH7kPOaB/cfnvN6CI7uWwQJgx+o9eUNCcTZSNmN4mO+jsmlEZM4P0tEj\nZdS1nSLB0GNXZgzb8vQzMrBL3RT9XtLg0MB/n4ZUgUplvTdBXbxyInjeyEJq\nI5ExqIWXqwtQTLheV7XHCu+kHAOwH1QHoxN0/Y/MJ84nM5OPRrlJFPKXZORP\nti8BD4DrLMo5f0CmhzGzTNr2+JF+8ErMqjHLACM4+lHj9+VOgzULCk4Jbu/3\nIQ8iIxE2pgDuIgpjY0LcBqofZtD7WxvJ2riYZeYoc/8CENXXKtis+tYqxuE5\nRwlIJw1UYwIIRDlbSP+PB9M+tUURBrl4VzZrxicoEYcDsFCjsSIuYzQdv7j0\nkN/56MrAW6eq/HIdTrOfrf385BPBJnVuBOZw9xJsp1TzkBs3LrR5x44NTH4y\n3cCBrY2G4nRb+SoCQpcwG/zkJ5YhazD2IFcbkooPtu+H+7LN4+NY+Vozzca0\npHVN9I2kE8YjEAZgdKDnJFqxTDf7pixytb1FfmyBpYw3fK6qqNhu+JNhKgZ6\nDjBOJ7ZvwTWVh1WOUXLVxa6C3s34NZZhpeQniPK5xVwXAvxTHvUhmoWAzsW4\na1rP\r\n=kvRu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEhL/XBreUwLqCVCI+/I4suXQ82G24ZDPC2UTUdSsRjZAiEAvrWgDqCAvHrs0/PN6E958J9D8778QmT9JBnr6ELl4n4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.2_1624962147985_0.3075107972072495"},"_hasShrinkwrap":false},"3.2.3--canary.145.19cbdddccd6f47d20e697d0051ad5d2443c3c1af.0":{"name":"@sberdevices/assistant-client","version":"3.2.3--canary.145.19cbdddccd6f47d20e697d0051ad5d2443c3c1af.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"19cbdddccd6f47d20e697d0051ad5d2443c3c1af","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.3--canary.145.19cbdddccd6f47d20e697d0051ad5d2443c3c1af.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-AggUuDjoMRqBStHbzxP5UArnlSgAD133FqM+5fXo3uf3aaLBEMYWT2jYWSv8Vcv1LcNKhEpdB8Lh0cHv0F4gQQ==","shasum":"b773ee6e672c1c6ea090fd52ad66433bc29507ae","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.3--canary.145.19cbdddccd6f47d20e697d0051ad5d2443c3c1af.0.tgz","fileCount":66,"unpackedSize":1001100,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3Ek8CRA9TVsSAnZWagAARqgQAKIRG4S3efC5BA1wkuhD\na34Sv0rx9GwbODMm5+2eW5oCp8Rny/4gO4DAMBE0aPyGdCXqHMqM5q85sHAV\nZp6hysMEqfZU7pHC8CpOMxH2vtVUrisGU2GzhJVE3rFmPWdMWIIuhu2hRqwd\n5dkVcBT0/S5cOoAaFJIiWUXbtDVIim3VR1XtTyPkI3S98NnFKjWibHTV/oHQ\nn08qxhELDA+uAQbuFBX71dvE9WgoKWfaHXoB61nt5er23S6Xe8F753IB1+01\n7Xaj0m4Yw2ItaHDUuPtkFbeuWHzHp2pDhNeaxfvII6RRlYuYtTOdjeUCNkQF\nN/PjuzRGhDBmJokOoMh2y+iglJWjagGQ6gtVZTV+dxNRpGobuKKTzwSh6OPj\nlVRX6DIPjU4lu42VHx9p6wCwe2v3lN3PJ2ubMxhsF3XRE01h7tk3B38qaAIV\nRsXKS9kMmL3Os+TjnTCOlXVCaYs8VUTMxey/dlXS/X495OHn9dfxgUkPbhPd\nlsC21JdHbfmra92Ko2YglGll93eeLoME/OeH2+1nKOYPFkK9pQ+bYeTqN7pd\nmo54Tsqk1Thn0faiyGqX1V5qvDnc/rnHe5R+m27xzA5aVUKBoIxw+66FIDoD\nB+/XKifvkWAFa4DxGqGJ7U3IRzBKilmANMeQk1dpcsvkDx/Ji322imtrkvpL\nl8Ul\r\n=uFKp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBF8tXQg1Ss+BGEDfT0fRRffNzu6NkyFaVU1VHo2btQXAiARKs8neCMuBjEMkmWp5kECOAn0gsQsf5wDZ4QZZdMueg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.3--canary.145.19cbdddccd6f47d20e697d0051ad5d2443c3c1af.0_1625049403584_0.08354816803970389"},"_hasShrinkwrap":false},"3.2.3--canary.145.59cec087ecf1b9908d32a19b05e495fc18b0c614.0":{"name":"@sberdevices/assistant-client","version":"3.2.3--canary.145.59cec087ecf1b9908d32a19b05e495fc18b0c614.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"59cec087ecf1b9908d32a19b05e495fc18b0c614","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.2.3--canary.145.59cec087ecf1b9908d32a19b05e495fc18b0c614.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-khwlv52eVEbjHN2ytefXNKbrpI26bkqQ+RXZ1zIm1lSkLfIOXH7PRuubO0xkr72hGiRxbHjYqvHFxoRFmmbNtw==","shasum":"5b5ba59869095fcc8ef51c462b7cdca30adc1e35","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.2.3--canary.145.59cec087ecf1b9908d32a19b05e495fc18b0c614.0.tgz","fileCount":66,"unpackedSize":1001042,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3HBTCRA9TVsSAnZWagAA53AP/RForlJZLGarAr1Gog8P\nMDvSVgLVO5Av8Ggy0/LNnPD7FSDKxXPofNvL144ylyHL0CVDMq7m7horU6z4\nXMMRgi9Rnqnt/eHjvFhWRuoAjvsuelzZGO53ivfJtivvsZvwZJyl69vy1pfV\nTTfp/Q1huQ0DJiq+sPMVdLzAtHEIiSrrGewOhw68tr8PGS87Q2smy2RFowct\nVjAUxlV2NEiYood0EV762Iq++cn1K/p1WijHYO3IWfJyGmpCvWqSM+4loS6P\ny8f7QpBF304Knkotj4kBGOccvu2b6+YwuYVTnpLOYcYxeal1NlHcNWtrXiHV\nr1hR3SfHYWdVn/PGVGg5/uCpZLgD06Rvoh4etBVpCMAVXd47ejsFkcobFw3b\nsA+U0p7deI8BX7xHRwgos0d9qfsf8xqhjR/ZhR+hY6HaXaESZ+vux7d6StNa\n+x8qWxLcujcRIaACwt1Rg5Kt8gyiAjs+k4Aq9BctitqiRKqNUlzbGaKZ0kBA\n/5FPp4i78cv2A6o/85hCGeNXcnsuOBw8HwLCIfgq/5L4l6E4/s/g9IFaGaMd\nQoJ12gqqE1afGeNK0EBvTG/NuCuaHmcR0YKcRb2FnbfWBoi5V3Loj0Cuvvfd\nT81JvaMTa+DIRezuHXAqeYTCsEF+dXzBeJtqMYrowx7BsJ5xVzkzWMLwZdkb\nUxXm\r\n=5MuW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCOmbvQL/5svlqic6FH7OkTSPJ5WEAtQU9FeLAlDMbTMwIhAPzu5TYgcDo/xh8UA7lAbgJdAYdV2UYF5PyragaiD9/e"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.2.3--canary.145.59cec087ecf1b9908d32a19b05e495fc18b0c614.0_1625059411325_0.5062342459557643"},"_hasShrinkwrap":false},"3.3.0--canary.146.9c210de4c5edcf9736b993fc04fd7887cdb95ed4.0":{"name":"@sberdevices/assistant-client","version":"3.3.0--canary.146.9c210de4c5edcf9736b993fc04fd7887cdb95ed4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9c210de4c5edcf9736b993fc04fd7887cdb95ed4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.0--canary.146.9c210de4c5edcf9736b993fc04fd7887cdb95ed4.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-xdOBXdNYe9xj77VB931L56Og9WpFcfJB5kmjVIyz/hGuYVSFVf65Iy9ddP7dR2uzfvtg9VAB66QMZoWKtUI9Aw==","shasum":"a6e3ef044e6e25d0c10d93aa0dd543d2568f290b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.0--canary.146.9c210de4c5edcf9736b993fc04fd7887cdb95ed4.0.tgz","fileCount":66,"unpackedSize":1000067,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3ZMOCRA9TVsSAnZWagAAPAwQAKGtpy96KIm8sZ18kY8Z\nn4psSUOV8nkOPA09KE2P+1e5084KrAIixy3KRVFNOWEKiBhvQpWlDNfwq1Ls\nE0UBwxea3LhPwudM/r5V1F/QOHiNP0HAE0U4fvTD2+zihbP6mqMRwwBKRZcV\n3YVW/naKNfKzHXaL2vboMYE1skKEaxYkRsmU6zSrTOMt5mGbTxJVdvcgeATU\nszm06Y7JrkB0ZFuapWKiFkMsvUTgxBRYX5XGRes0AwMKqWilttxxRe7iuAoJ\neDjrgFiIzhqIxVMSIPT9RZzpOuxTljTMiWHjqPKFosD6up8m4sPOxenfX5LU\ntuAojQLog6F8uDYgkn1ovtIjGlX5XF+sik1f4xcH08A7ZbByUkpT4CId0zJi\nZUB2LFpXrMojkFFcHEjWlDgxRhLqClD1fXODJg/fPaDsmAVIhM4efhljDuLB\nrMZsNd1YlSlykoodXUKPI0G3Jh/hisTlEcLZEW/xUq+4iLNnpbQvKvvJLxfv\nKm0IyXvJlsafLDQDn1MpiV5ESVM+tNaMCGrJ9vtNo1WRvXb3na8Qc/gfrsC6\nBO1rLvi/UFaoeHWTWoXqzQSWsede53yg1trVxc1UFd/h4rXq7P+aajzUf2GL\nfWJDrJSGrxAwajt12TAATqM1RE8HY/xsWF0yiHmfZ0XGXfxljES9d0shvqWh\nNSXt\r\n=9qjl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCfBZZQ/uVpQUOuiBdMWSsbiw5PIEh6uHirStpUgL+fWQIgP1PSZUID25bCkDuCu1BLSdA6bLABnJMqn/S8mZ5nOM0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.0--canary.146.9c210de4c5edcf9736b993fc04fd7887cdb95ed4.0_1625133838057_0.680288268542435"},"_hasShrinkwrap":false},"3.3.0":{"name":"@sberdevices/assistant-client","version":"3.3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b24e1c8dd4f48a596166212356b6a3e9f3566b94","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-FIn+8xLxWTZbC+Ig7vK9KzWuCYuejVQ7oRcSEvaG1Vm2i7PpblvSitvvmdZgWhqPXcBymFde3XFZmjHIhYQ7Hg==","shasum":"7f539f5e1d2111165d473d2eddcff43088ce852e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.0.tgz","fileCount":66,"unpackedSize":1000223,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3swwCRA9TVsSAnZWagAA3tYP/3HWOS6PpY2ftuR7BePi\n23ODlQIDdYozUNunn3l1uL/7i2SXomG4SUhamdiH6KdsfeWdL/TKPbno7e9N\nO8E4JsS3NaSHDX7Fu3wBJxYaiNePSF5U/67UuQ25PStVRS9nF4jqVNo7D8jo\n7g+jj1+9Np2tWS6y7QkwRplbtuknXDveRO+SWTor6i7GgGp32j+vDLB+lB7u\nqFI/GMqVQ3yI4lQLyLpDc0pSWgZzZ20gp1Chm27DrQorIG2fKpTpAtMClFTy\nhyY6JspYNTguVh9pvB+U4erZg29c/fpiKy7MawCNaz1jLVxJgzSHQ1TD+vNW\nzjS7kWns+Nfz+7ZOuyhZciqpU0y+mCopX/wmrpoERGqtVY04xOWH+l3PIWb3\nNLvuJhqYiKum1uVfXG9pbwuGPm6xhvHnYTNxu84VuVwQ8jeRHMijD5CwYZTh\nUj9iJhryc3CYf074G12yFhsfw50Kigpu3ajjhhe+lI/O+MXguopFpvyELZpe\nkF/peaEDsD+vrCp5K7YJnkvuTLXTTSsewaB8SApPG2NgJfJAJEZHuXsrRlOZ\nqIWcopUztea5oaufGh99aMZxATFd0CgBU17C8gSWe+2CmNe/kzVO1MNx2qAI\ndqRZZhTOLcAT9hC4BpsYSbtV6QyMlNQDz2rP4Hgyhs4uXqAav4KeyhETeN3p\n7MRI\r\n=dNtM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBHrmHW7eQTYURyTaJ8Nt1M5AZ4xhZOGaaevDHtDn/WHAiEAhVvD5B6Pnt0pnVxG9QVft6Xrk2ZYEpfhEiPPT4NttYU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.0_1625213999361_0.8948471004690957"},"_hasShrinkwrap":false},"3.3.1--canary.145.6472bd7d5981057b6015745f12a9cf942f58499a.0":{"name":"@sberdevices/assistant-client","version":"3.3.1--canary.145.6472bd7d5981057b6015745f12a9cf942f58499a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6472bd7d5981057b6015745f12a9cf942f58499a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.1--canary.145.6472bd7d5981057b6015745f12a9cf942f58499a.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-fZTsOqJxKMzU+tdqc9iw85pq/Q3doLg29H3GsifE3VDsAweozgVon+tghRjINzNVlIyLr5ts4voT5HYDcEvzLw==","shasum":"601b816f31023fa55aaff21096f46c5d3ca2c902","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.1--canary.145.6472bd7d5981057b6015745f12a9cf942f58499a.0.tgz","fileCount":66,"unpackedSize":1001136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3vAlCRA9TVsSAnZWagAAM1QP/jxy0Td9+9JN7wUd17T+\niDeJ07pt8Z0VkfZdF2ostTfsMJg9OJkZgT4uOTfJsuxCq/6L5B7o5A00vrpS\nrjh2Sq4D9BriJozG/zVlKlIg9FvbAidNiugVamPbl/Pn6Q79DNAYla4AKDBl\n8J7GKGoNS2CPybRB3X9vnReb8aCP+RlwwlbU5CrhbYF95NtN8MU7hCa/t+5i\nQHdUqtruYq+N5MP2o1o4H0/PvR/eT/scxgUry0qVt/4aLV/RpG5G4PwB9On/\nZgb9K6LZs2yTHDqAIAlRTXarbOC5kMyFBr9JOYLp0COwjYvZhrapP4H/4eoU\np9Qs+FAjYvqC9X5wK1RZG06e0uo0hrxWJhhAGoe/Ywq043yj0s1ZFgL2m3i3\nXsRpdIkYBPbN5h/ILQLJhFOgppv8QfiGqkmP5LrFl7X7CwfwCUnTQdhHl+QE\nHO9BjMgo3agaTSu09yRAmXTE1ohUcz4BvgWViu4Fvnokw7AnMPy7qqJnxSnI\nPxl+2tFO3EJvsVNuOMp1Q73AM5qlE4eixI+wPsYy6TNpn8ebKxeEm1P16wSs\nafsREBDVV9t8vYF1xDlvHcGVIQUkXg7LcyK3SexEq0ZwfKT1XPe2VjANzSOS\n2ImgyEKtzN4K8bmM9c1mM+2dj3Z/WjyinD1sf2sIqibHirzeQpwfqWJciaFd\noVSQ\r\n=rwCR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDdx5jvm78CHlacnixHMw2elOz43kRJ0Qxu6Z16Qxp4zQIhAMgnTUFBXLZfwejZQTgzWxRBA1HvmSZuxZmQXrHzAXE2"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.1--canary.145.6472bd7d5981057b6015745f12a9cf942f58499a.0_1625223204582_0.842664198946514"},"_hasShrinkwrap":false},"3.3.1":{"name":"@sberdevices/assistant-client","version":"3.3.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6f1f6f1a9e95fcd14a42ce5a7818e900a6a09f7e","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.1","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-A6FP/IdAdTbBXE/VlmohPkGe/mXzqpai9gnrnNuTbGRjHyXwAgeHXk7oCQt18mg11DV/6zXHKmyQYM2MypEr2Q==","shasum":"3cbec49bdded6be052fe82eaa9f9050915e61704","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.1.tgz","fileCount":66,"unpackedSize":1001342,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3vD/CRA9TVsSAnZWagAAFZ4P/igGqRIXhmk0labsaS70\neEquM12j+Xpr/8r+lbDqwG50wcNE7/b2HOY5c3yIc0iGkoEWuaiS3YXQ75YD\nXFBZGhxMZAb5HqAQFD+iF/sk1xF9+5LoHDifJDLVajBg4rVRtI7fbmuoH+nO\nGwtyO9kJ7Yejdf+GGn5XGa9PmC9/XfI//WcjC2KTrR+RWhsbQo/DInRdJpQI\nX6J0vRBVU2mdLINfK8aqKoRTh1PIoMTkidDvMY7+8GQiUgZF38JVJWSj17Lr\nXDBCu307P7ndMIK+MwVKkyOz0K6/96wKLN6jKcbi5wB/nr2l/tsi7LdQyJOo\nTlkOu7Ljo1J1D1bqhBB6MNkfbDwa8j7zSTXOxhF3lwJ5m4i9KCdTQaN5qeID\nqr49NgRmzdnSt6Ow/NJIGYKoX0PCnuZtTXWpYXKvVDn7gKcy5IDwTwt8W5MG\nQ1WiQ82xGmDoqgPq5ZzkQWEgXHEu0EUe94X6E/LRh4QMu5CmOMM0+/cMe1+W\nCAv7HJrRMtTejkZSlv0nEVBkX9+8PW0wVKZUvbDX++r5Dst6s02wx23GLpM/\nS+kAb5YA8XODuIHLl4xOrXMT7kpI+io4m7yfB4c+fOXcMEOG+tR5ZwzGC2lP\nDx6HQkCDfEUFHMtHeej1MWgOn/lyvIvuaQi50/we84jwVgcJkA+wTa0F5iF1\nzt8P\r\n=WslR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwk+k/CvYHajo1V5wSPjgaOppvKiw+Xh37OOPtP1goEgIgG6J1oW8qpF4U46wXXps9Ya1rl5jNumCKzxZZ5RewwQc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.1_1625223422906_0.42936192248719607"},"_hasShrinkwrap":false},"3.3.2--canary.147.46ae3987fab8944f98c84398fd848f148bb61d73.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.147.46ae3987fab8944f98c84398fd848f148bb61d73.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"46ae3987fab8944f98c84398fd848f148bb61d73","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.147.46ae3987fab8944f98c84398fd848f148bb61d73.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-sfVhEeag36u+I6hoJmxdwaUaa2MDqPxwrc+NN+Hj6BNY8G++RVB60gmhIqbpfDHSmmIYw+RGTP1Fw1RTw2ZUXQ==","shasum":"319fe0703e22acf67d268c5f064cfb31950cb91d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.147.46ae3987fab8944f98c84398fd848f148bb61d73.0.tgz","fileCount":66,"unpackedSize":1001959,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg4tEiCRA9TVsSAnZWagAASysP/jNzfSJkDsIF1x+26xDt\na2fKQo6CLSayp5pHPGvPUKm7NXp8PiTArNywxqJg6nSGdr8BTE6scCPHpJbg\nVZJx1M/QvYSy6bx8r1og2cx4u6ryP1FRdUeHyTgFv+o4MXPeOPES1R5UO6my\n/Xwz+fDenu05FXysFG2Iw5t9Kakq9s37tC+mA7Usb7tA3GnP4Y7+ljUDuZ7E\nCcueGEl0NX8DrM4t+kGrr5MxfbUOKNa1+jvKCRCnLuMb/AK/D4QFWMZZlYZS\nkuToz9vqSyAIUmtq6vvUTkwhenEyVuW168at+6Oph6GQiS7POqVL4ixJPJOU\nyNBjeAku6a3oZy4dt/HOHUlbp1n0o/a7WrF05GsVqRo08h5gu3obnnRvVxW7\nP94tOGz+xPY6E5RnVhkJcfa+0+T5xxnKvsI41ny50OtkA0VbtPwfJ6be9X6J\nXsq3BfGAGjz0C/CeWB9CT7jsDDX4eAnA6ovt/n/eDsyx7NOIHuJn1bg2P9Fs\nMSGWeYGgu9vU/eLkbkgmq03DYjZ/tkP4YMfkQ02vbCGzUmHcEzpK9xn6QUyM\nZsK6SXiEnXtBeUoRpex+oRm6652PnQyLnQ+nq+/Y6OpFwIFcdhlJwT9u4btD\nW9mzplX8XZ6x0VP8NQKM6CGYMUjFr9X73RBqCWNd7n4LNEzT/VwRO6Dlr8c6\n+gd0\r\n=AxIn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDrb5B6FIZvY/5CdtE3SazZ1QXOpRpfxDw2JHmXqeMDsgIgJGv1ezCUJLbZGZ1IP/88ulE+MUdCvC9+4z6gMAcx4Kc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.147.46ae3987fab8944f98c84398fd848f148bb61d73.0_1625477409793_0.610639496108754"},"_hasShrinkwrap":false},"3.3.2--canary.147.baed5e92bd48f7ac0a9dcf71a58d72019d59b038.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.147.baed5e92bd48f7ac0a9dcf71a58d72019d59b038.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"baed5e92bd48f7ac0a9dcf71a58d72019d59b038","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.147.baed5e92bd48f7ac0a9dcf71a58d72019d59b038.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-mfm4+e0cbM2yuMttNCTWOYbPtc3ZHQo/sNHimHbLtbog4vVz1oe4DrstH//Vlt9536MFdYAVDrSPFAsbbGEQmQ==","shasum":"8c3e543a18d7d3a67eb8948cc7cee7f3174f413c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.147.baed5e92bd48f7ac0a9dcf71a58d72019d59b038.0.tgz","fileCount":66,"unpackedSize":1002318,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5B81CRA9TVsSAnZWagAAwiQP/21Hns/PcFArrM+G93GJ\n9FNZLWC6ZVUixA4dw73wYZqTsFxifs2wL3uwMf527Sw3oSbPJC8ykWEw6Rfm\nGCRDmsu9OKZraPilXBdmTK/RW/Wt9KzJk55t3eYkrkrHrTgZMLiO2jUDlFly\n8r6XWnd4uL5kyPrgzpWqp+ouZhvaECqVLraq02I1OzLUZ3rsykBYuZYEL3Wj\nkbsJjcobUljqVOvVT1FJd7nPic/Nlz4Bm3Ubb3iwX0rs+mttd/ykdoNrhghd\nGtWL2rJVM0gO+bP6S1SHMLCIDTzbXa2yKYjl0MLBQP/eTxaOxk6jvS22tMhk\ngIGDnVQ6KroXsVwPYH2fMM3R1SGixaX1BwaKpXkgyKSr2kZDKuyB3017Ty0v\nRozEPORQD3xjZC5Kv4SqweOh4NbTiohFqus/ge8LvJTKZFDVWfPjAEbQPwvo\nON1+MRh6iqqYYCpg2TB5npzmHh2GhvHWlK0lQYUi5GglV1I9VzX2MK+u9RIy\nmlisWCrZG9L0PVKQlRbUdLBHocZBVCSvQPxaxjviUJ8oCRHsJ2DSto2H4OFu\njeDKeWb5UtdXwlTGrjKDRQFR6l44fOVGFOekcln6tq4lMQ2fuVGD3hzOk2zf\nP6ZXT+USgFE1NID+84IHjAJUEfxxXjLEcZiHocS8d/ESKOdY57vaC3KVTPU7\nE1xr\r\n=4cmN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAM4TY8vwKfj86I++HKHFiI50z7iuaetSRJPkGq6sPVeAiEA01AbF0VwEqr83xBxydWRtK7Ind93NFZp0huD984wn3g="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.147.baed5e92bd48f7ac0a9dcf71a58d72019d59b038.0_1625562932377_0.7414452236254365"},"_hasShrinkwrap":false},"3.3.2--canary.150.accfb890e03133b6b51b97a9e81bacccbf2a4e48.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.150.accfb890e03133b6b51b97a9e81bacccbf2a4e48.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"accfb890e03133b6b51b97a9e81bacccbf2a4e48","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.150.accfb890e03133b6b51b97a9e81bacccbf2a4e48.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-eYOu2HxyT3mroS0mpBZVprVLhkOADEKq864AVgkG5fEUlbcLnj62GFzBnndaq8HP+oizP1lSgJUXsu7FLt9zCQ==","shasum":"84d463f5b57f363499d76b94d4285b498462262d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.150.accfb890e03133b6b51b97a9e81bacccbf2a4e48.0.tgz","fileCount":66,"unpackedSize":1001432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5Yp1CRA9TVsSAnZWagAAUPsP/3pcobj8ho215Puc4WEv\njRSpKO7+Uvyvnn0iiI4jufgbLMirQlul1DOSiByVhSS2id7C64jSr6wT2aZK\nRra1aPhIUJiCKZhVCpAhL7davGYOEUynwZbt6DqaQoAp87Ny58mMY+7ChK36\n1QwNSbH0kI9rFHi4apQEEPWkVaemF4hTqCiu9FyVwoZpQonvWj81B+hLvhgK\n2QZjVeTmtVBbQKnbwVlWq09DkBBwz3vKVJAhlenArI3LgyC5LTsTQZFEEtKF\n4vV/iLBHFagIYpOOwAwTvmlg1kuJj8SYxJm4Ja9P5nLlcdeIq9Lh4Yu0EYeo\nZJ6zK+6AxbXaMI3y/wZeQMG15uD8WQB+OQv/OGfkb1wcJp92QjEQai0ue3Z1\n8vDK+7FYXc2RvWY2XmICUGhoNWV3qAw8nxwFXyWGuV7xq5Z0OrrJjlCgtUS7\nUtb6TYj2NAadJkO4JJh4CiaTwCtpjQbWOg2giQe0Hv3MzybdaijE1sAmGwsD\nDNfF6hrc/SR4AByftu1B3/hNI8H73gr9RjJvqcNQKQ0KwTo7SYcYC0G5SaCs\niL9vnooU1meWSjatIeXvrzSUfRwvTAf+6LXRWP57EJ4Gx64dS3rkS+cHFDFG\nrXJtLPWQIHbv9iHR6bASafXT9EjdeYDlfsiMUeNqXlG/FcGiOiprN6RceTgg\ntHID\r\n=AZV+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmy0bxgBgzMUstapvBBKlrEiGKG26W85TDdrced8zgrwIgNYXgOGXr8fpHzKfpH/WaoA2Meb3QeLdgzZn0Q9khnH4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.150.accfb890e03133b6b51b97a9e81bacccbf2a4e48.0_1625655925006_0.7596606643004802"},"_hasShrinkwrap":false},"3.3.2--canary.150.4ff9e0d98ca84cb8504e29b188d9419317fc1c63.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.150.4ff9e0d98ca84cb8504e29b188d9419317fc1c63.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4ff9e0d98ca84cb8504e29b188d9419317fc1c63","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.150.4ff9e0d98ca84cb8504e29b188d9419317fc1c63.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-7OkRims8S6/YZzdJRqU23O7oeLZaRbgF68w0HPG0kwwFhReF5dNL6kPSRhETfNXXuW9jdYQAJuI2M+s/aDnnqw==","shasum":"a687136cf2d6b56acebd642f425ff6140897048e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.150.4ff9e0d98ca84cb8504e29b188d9419317fc1c63.0.tgz","fileCount":66,"unpackedSize":1001432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5YtnCRA9TVsSAnZWagAA8EAP/39xSAlRRIO1+WC9nNRG\ngSlFplZz7R92A2bxZmRGiehcviRufvlA128dBX1P8mYnoYahQ0CeFhW+Kmn2\nONVWhDVfr3BckO9BUIoHl1Z/jA+6kOoDb0ILq1k+MsO2GIGJT3vHMBH9IWl+\neRjN/J5JSMsbYC0ZEyq9aS5ihBSW5FAXs9uaeLy8v5n4GeqrGzbGWKkEYoUl\nLwJM0XyWz/Kj25u8zNzT+bzfFVsISM6Q2yAriqBmNfZHhftpjouf5wuYs/eG\nznFDp/MI/N8WrbS48l2CkEHmpM0AbUP9v3Ijs4FFF+hhSSWeD9iZ4j1GBUhU\nI82ldPnRQI/urgivnc1m7Yce9zGPsvhu5gRkPkUBHkvHnl3vrsFas3jUG1Lk\nR6xYCJY5uoSxVgjvNNzMg4ipjbuNO/G4HnEqqTISBraG4ziLCyfO9kuTvmwk\nlNgHI6UKTTBrAA5Qc2dXf/i1MY+KJUx4283wkQ0nbFFwkzsfRVbZ4tCYJK+n\n8uwjQiUgJ4Q4wsogUD5k/JRCb6MmEpVf74O6eHfCb+BDtZ/te1VZeHil1wOO\naK3TmLecKfwkJL6oFp0NQDCWR+sCgNuIciCbP8RRv1sfrNkrsZUr+x9CZ0pG\nqEZJbBAwq1scEI50a2N2FI+05lD2ACkGZQpqfAK4unnOrjQXwzeGV2QnmQFk\n03VF\r\n=Zba0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE5yvJCQxrUYVpzoySzYxEj2ye/cnFVQZ09nLJJzofPXAiEAxVrXeWB3sm61NIyaDUAEKqHqCy4FJyYjFqKeD6/pwVs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.150.4ff9e0d98ca84cb8504e29b188d9419317fc1c63.0_1625656167469_0.2575320430831003"},"_hasShrinkwrap":false},"3.3.2--canary.150.9fc6423739d5691a5593b48a46487df8a19b3d0f.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.150.9fc6423739d5691a5593b48a46487df8a19b3d0f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9fc6423739d5691a5593b48a46487df8a19b3d0f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.150.9fc6423739d5691a5593b48a46487df8a19b3d0f.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-wpfU4oYrxwrgpQp5iU5fvMOS06uXcy6MLRfIT6BtALuFvMvl0hiMOxDE+6fTA49WTDji8I26du+DKGcYOtAVqw==","shasum":"7593d2a01b33d8a9f07178e217e3b0151e77c61d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.150.9fc6423739d5691a5593b48a46487df8a19b3d0f.0.tgz","fileCount":66,"unpackedSize":1001432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5ae6CRA9TVsSAnZWagAAYRcP/31HnfqlkziCgZEHSFGY\n6ITBBE7W4hI2t5oKcfc2M3xzU7iVODUV83/KaGdmIcLuqGEcnYgXRL4GjBur\nculEZqDu/2kJPqX6hewn0zB5XW9zK20MnCapgX0lnzZrMMEVTYosGnHc/BYp\njodY34fq55j9GdJs+F8LOBVQvgA7xw/faWoTRo7qPCTp4SH2NNmPb/IxHzTi\npfQ3uV3u4KIMTmyuv8xO1VSthvrLUDr3fm/TbGuceJ7rQ8efrLNpmvuzO8yK\n0QU0g8zYXlTk1miM4qK/REgVCjg9AMxR21IFYjqvY4X5sormh4ZO0wStLBtb\nupz6TdDBaWkexA6cNLFMwPJ4s45hzFcLn1FlGgxLzqh4KeLRzCc76/zimnmT\n7YbpuIAW1eDkNvISxJXcF+oBYctgZ9DaoLn40OWG4LJ1ExprVS2X7cCv7jG7\ngXUH/pJhjo2Bua8shDQ9wOqZrOjr0xioq0jIrQeE7eXarTV3oTqY9tO5GW75\n4KWeXJJ6r8z1mfbxpWGuSZmN4xATFivahv/v8RW0iDs86fXaBsBTys/PCIGq\n5bCRRCc1IEUqTznmLnDM7urZ81oZ7sp3o1yPZV6Zg8nwjGw3NCO/y2Cdiq02\nZ4lL9eB/Y8Qvdie/LzDXZTASFpHHV5KdW9tH7o3eouCqg4SQ6e/+NExlUyTi\njiAh\r\n=F+Xi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCow34JDfsbtx2a4MabxiRXEmwK2RfPIKVKwG7sllQz8AIgX8mbfes5Kc66ZlqRuKteTH9HUWotoaep1F3b+FQI7Cs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.150.9fc6423739d5691a5593b48a46487df8a19b3d0f.0_1625663417762_0.17732106975010908"},"_hasShrinkwrap":false},"3.3.2--canary.150.ef085824d1f72a8bf71f9a7a161c9a24ed405b9b.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.150.ef085824d1f72a8bf71f9a7a161c9a24ed405b9b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ef085824d1f72a8bf71f9a7a161c9a24ed405b9b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.150.ef085824d1f72a8bf71f9a7a161c9a24ed405b9b.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-17ZYmWr/8rvMFnns9Y+VTsamQpDIQ5kkiIZaF2tvyLdc5Bs2Nt0O+1y6gxV50n5PWoSre6EAzuxkCyk9MgGI3Q==","shasum":"8e128ccb86a35c042fb4c42dbe6740f032436cf7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.150.ef085824d1f72a8bf71f9a7a161c9a24ed405b9b.0.tgz","fileCount":66,"unpackedSize":1001432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5cYDCRA9TVsSAnZWagAATmYQAJa1u1LC/TklgemLT2Jb\nBAMo4ngEHFQXrTrRyrHdtVwJ21y6nj30Qi/b162MaPJPh/rnc1s3lh40w91V\nDgBwpw7PntGz67aKhxXXllfgTQpUwUdRiixqYJLQHMChcIhQmU849SfFkXKr\n9qODC2dJ88h3Z9Za1LzrkPSCBgR8b5lzwLLUhH4Z1LKDTLFhYbWecQN9F9gf\nFERZxRkeKxRSX+K6R/3aQ2vnoEoPNorpPrBEIzK1S/L/79NvZFBy+hDLpWY8\ncPwvpVlhfab2ECYem3X2lzLU6M0o/JCwws8h/7b1ZLbuP+Q/Jn8j1IARqsFu\ndCQBahxnjxavrVZT/FXzZbNX3ZxA0a1TTIbMPOVS25ytFwwBYusDkYI/xa0D\nLN5Qvat76jTdHS4SfzfyoLcP0yOzwE9V74V2mIBzrpZNc50PMQTyjACazM5d\n2AQMvGG9wbwLHFTtCTTonmKqJ6R9XjM5ziJX7LFBTMaki+CCJMebJMhCMh/p\nJKMDE000vxwNyrYHSQatS42yVUJXGVXYMk+pqFjmBYZmjUBRz43vQpGeI9fx\nA3XhZt2dOLU5A6lRA3v3No8UxL98rdd6B19RwHrCZhXQEm8Kmc5ioStzYrRB\nO1DF5RBbrGTyiosZMJn7Sigi9zIJsW6vkWDHRD2v9NZ22FEkwPfosV/b/LSF\nls5F\r\n=NMEY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCntxu9Ly3sUuW2uyjgHID3b1HUFn9zSFIPYTp5TVCEmwIgD/klG2Kb0QqHJvMYT0j0fxKorz93857dOYsKfy3pg1Y="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.150.ef085824d1f72a8bf71f9a7a161c9a24ed405b9b.0_1625671171192_0.36442897426757015"},"_hasShrinkwrap":false},"3.3.2--canary.147.cd171daca04dfa6a21329e031950dc28326ef780.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.147.cd171daca04dfa6a21329e031950dc28326ef780.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cd171daca04dfa6a21329e031950dc28326ef780","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.147.cd171daca04dfa6a21329e031950dc28326ef780.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-2TKqNOD5r9at9hAH0Wactc58DpNXoO3k0DE/yGlZb5u+/lhxYNWJ3dMR3dUiICbRNlGkHQf84s1grNpe1ml94g==","shasum":"84af0069a432c6678d486c85e2235a733f9fd147","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.147.cd171daca04dfa6a21329e031950dc28326ef780.0.tgz","fileCount":66,"unpackedSize":1002679,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5rAZCRA9TVsSAnZWagAA2ZUP/RaE3R8B0Rl4UBCnN79U\nVtZO6ivHMbLrm/LDvPRTl0uZNfiPOFf9euwLspOKHnuMK73IgASuhbgxi6vS\nVazfSLkxyW5MFCXlGZr33tj6V27kFVkzsTdTTAv4gTqKRi+9NG7UcGlVgL4K\ncJ50cNxSOcDvmd1DP1gyokbHU20xdZN+5qvqIuETagDICLe/AYVd+San3Bsw\nM4Qoav0H3XlDike9EXdbEJCJJNiSS22viowMooGRNxH4o+DeRyOejdZKrrFg\no/VML3zDUz7NZEV9Ilfv5JkK6DQUMyBiRwDBC+gHKUOshoE3rIoRpFYs83Iq\nY6j5n+ngpRFYPR5Q18OmGZXEFXJBuNukKoEdXIfMBMaOwveWFuRfVcG0bxDO\nt4oQQcn3uXtLUDtljzIkWXq9gf8NT81PDdsybrs+106/nBF4qs1pwEXVMNB5\nBGe4q50W3Q7BTavroB3upoU0/k+eT0o15iM/mztoK88Vdxj2ep030MBNK+A5\nQ3R3qidMCvGDzu8BNLjLf9A9e27BgASQyUEU6+fQPibJoR5gvcUjf/2omPhA\nJfQS60zAYONPbVi6PoFYYDq8Q32Xsv1qUTyLXSgI3bALNB0uJNCVdZcxlIMs\nKmVbN2CU3H1oV7I02cWP6yGylJ1vUoHn1iTPUUVMmmg/GTQ2yrXN05tQqx80\nyXHH\r\n=D7Yp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBhzQKW6A3ZHacamuP10YVtlzift1qCb/07Bw6FV2NX8AiEAhwq9RsbalMo5/GK7U756ZDHyHuWOwIdiscaxz1IKsnY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.147.cd171daca04dfa6a21329e031950dc28326ef780.0_1625731097316_0.38539586573294193"},"_hasShrinkwrap":false},"3.3.2--canary.150.97740c88f77d62d9c16e886cde62b25d69973eef.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.150.97740c88f77d62d9c16e886cde62b25d69973eef.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"97740c88f77d62d9c16e886cde62b25d69973eef","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.150.97740c88f77d62d9c16e886cde62b25d69973eef.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-T4lnzTDZnskW9oc+AVuimtLC47DQf8wTFtrE/rLkuYJY4z2rJnQX1ndHDYXRxEH+X3XQQsLGlj5G2px4H0JApA==","shasum":"c8734db4f51f0468dd6d2e4aae65dab9ae2fb354","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.150.97740c88f77d62d9c16e886cde62b25d69973eef.0.tgz","fileCount":66,"unpackedSize":1001432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5rL8CRA9TVsSAnZWagAArvoP/RGz1sWW7PtUY30bpUNy\nvD/UnHjb+IFEQJhuNf1MAZ4fdUdUim0Fzs7r+/20w4S8kJF+0OF/4/Rln0sx\nanSMv1MgAMBHPAYRWmniU9/nCEvjEOiev4YXmxYJTgJ7yIGj78cxNxgmez0y\nC3AKeVry/rTN4HqW+35PnookMVnFLHp4NitKOQ33EGFhNz7bDGwY0ycSKCSE\n1kfLdU38f65f6QRvk2MZoFY6yNKmWhUhgXtjhmcuIvdbl+0esVSqIpXdjvKN\n63gCnqaK7JBSMdbMV/1QHkIRQKEwQO4/mxwSwP/rLagEi7dIY/col1k5Bdl/\nVZCLnXWExhwnt+Krb2iPGr3nl5KRnW4m6pGU0gwPPB09JYBeD09XL5Eh51xx\nqxPqdfp8GcKFsQElhuXAv9qhZI5XYs/Wzd501EVXztoyPfzmIrtFOVNDG59N\nB+xp0kRADe8QlQUHSj1uU63EPw72ch/fvswvl1Sds/TLSefwiHQxyFMTJtGx\n4xNqu81mFPurihx9wdjmHbmh+1bavzRRU7VIeHvcfR0AS5ByikMpS8Zbk8IH\nK8RKzaoWmMab3ogf6dub7qOxUVNrDlHmdO2jUrsfY/uZS4aP3AMZZcTXcpu9\nrsMWBncDKzyobgS8wamRw849nnL7Xio+3wF9bvbrIHgB0j4Okj1gJBUsDCAf\nZvxT\r\n=I0El\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD1ALdE2LQjzcX1ozsr0SPq9LcpF5VTWlaE0g8cA7w9pAIgFbZEJFFMbPad9XBDcbs5S0ZEgS/WI8m6wihZ6XwkhHs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.150.97740c88f77d62d9c16e886cde62b25d69973eef.0_1625731836169_0.020092702655520434"},"_hasShrinkwrap":false},"3.3.2--canary.150.4f47f4fed553266b572e4094bf6bf956ddc737ff.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.150.4f47f4fed553266b572e4094bf6bf956ddc737ff.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4f47f4fed553266b572e4094bf6bf956ddc737ff","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.150.4f47f4fed553266b572e4094bf6bf956ddc737ff.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Nt7i9lzZINBltHY3VJX68Q1KwDHJIctVOFSyHRbNQMhyOUVCU7kScZA+babhHq1NIGc9xnfN+J+csDSXvdV+tw==","shasum":"fc4bc46533727c38c8a86d21693a23a913609ca0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.150.4f47f4fed553266b572e4094bf6bf956ddc737ff.0.tgz","fileCount":66,"unpackedSize":1001432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5rf0CRA9TVsSAnZWagAA/0EP/1D3wRVFWy6N4lzSfQ1A\ntAn+Xczz8ioDBz3SS2jASSeXPH+7mweWTqqMzzlF4SBe/LKgHBxLszprgv6s\n0vA8hJI8jfE4YET+Itn8KUwskCMPHFpySOn0UV546hJhbs44mTmm4G+VqaRk\nenXe19D+YkOMcByW+B0YdbCZU60JD+V/bj/kQ2JE1+XXY5PlT8v8xlq9+HFC\nVifKKt/pzYHOp9WI0nT8tw/Lchc8aAQFLk9QnF/wd6e3DH0fsxr3myAy6ooC\nERTO/k+r5yIR4vq4y2QP5Rw+IbPYGnrOpHKOWhMB5Iuz7M7EDRDmEJs8AFoh\nGOrlC/2rNzgHiUOQFBmgyljdPOBwiS0Zb7rqZAwBtPsmewMrqSozYvR7GzQ0\nNqSYyEY0hBzRIvbIdoQ6NmX6+JGq18HwcmFs52KAPr7w/mqqE9hiPaAhOB2+\nJswfqLM25t6rpXn8Vl4n1OOZbxu2CAPwTPnR4guyBYj9Tj/xlg2ZaCsjbaoV\nmDRHfpGzfgnHKTywvD//a+ORtJEYLZhfTyd7Ww6RhjNIswnyFUTw++zxKNr8\nEKNOI/JpnRh4/mKzXEoDNBCUS905Zg7BVH+TrMs/x6QlAHc4319Zwe/2LI3e\nkI076Kqc27YJe5V2C9rKVfZ7R2gGVqjtwQqrP0eVaxn7r0RtCwixQ15tkw1v\njf5K\r\n=CrUs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDUSiMn2ncPm9bxqW48SPCqbM+mG5JD5jSyMNZCG7G7SQIgT8ACEIi028jUDDDcdBoMefMraX0DbIweljL3ijpT8Qc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.150.4f47f4fed553266b572e4094bf6bf956ddc737ff.0_1625733108233_0.37116179166481245"},"_hasShrinkwrap":false},"3.3.2--canary.151.6c14f250785aef545f784211755ba6390f01e2af.0":{"name":"@sberdevices/assistant-client","version":"3.3.2--canary.151.6c14f250785aef545f784211755ba6390f01e2af.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6c14f250785aef545f784211755ba6390f01e2af","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2--canary.151.6c14f250785aef545f784211755ba6390f01e2af.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-cGF9ufPIWoGJNaTc7506M47VtqdAPiL6YpcnGxOtvDz3UqNWLU0l6oNw49WqElOEHliKFybV+3O4FvVJ2d6jiA==","shasum":"a2339766467a4eb68058153050993ac24d4c6c4c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2--canary.151.6c14f250785aef545f784211755ba6390f01e2af.0.tgz","fileCount":66,"unpackedSize":1001544,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5uuUCRA9TVsSAnZWagAAz5EP/03hXIsPhKc4WfOX4yI0\nU3is6s02hkxIbuwFBqAlukoiKsrgznaqr1FXZGpXv/wHGE0nsoI5FQ3aItZS\n7Sbk7gKK8Ve2hZOsyyyg38fksU2nC2GN8cn+uaJ4OeEZ5t3r3o8nYe9C6v+N\nD7brXEGFTNRXPB7WMZW2LMGAr6K6xczoqhjsgjB8DLZ/a1dpViZ9Kry/ppdz\nbJ7cR+FB1bNeGJgZHsOLMmFuhMtU/rlGfpUR3zx2zAssE9hSdumb7lFmnVeb\nBlvZhaTSaFGT+Acz86vzP+Ct+tFpTAC2gX7waZoiGrVegx8qVnNjdkXf1qIw\ntD6s4cSYf0ALEEBNloV3tOjW66lawcq2G84MPTKd6paBskLLpeR1GftzSdUt\nOxUCer7g92F9dzey97bhSA9a6BnORYUTS2FZba+thSBTE+/4fBOCB4baWbVz\n0uBa7NA/247Np1BXHL3pBU9OmhoYWGWx1OOg0Y0mMwrWHSMn9ivvywHHUqI3\nXhKNKqTcohI52ZjgFXnDeE0vjzUDT9AfQ16S7vJ7lWwzpNOB15Jr+a87sh4A\nR7VqeaACSLEMvqKihdcJL9lCQE8jc51YFCtPcDkx5GKRpWcnV8eVFP3Wasmd\nqYGaaCsihSofUpD5hwbGbIAmYm6TJrUKb0JIBFrp9oGGV4uvxxS8OU0lFXWn\noIhs\r\n=J1cH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCd0u7fWs/kw30d/W5J0vK/Epk0zblME7aDR+bvSY2RHgIhAPig9PvuCeH9dwHQiWoD/5cBuF5Mz4F/+n8txEKvjpbL"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2--canary.151.6c14f250785aef545f784211755ba6390f01e2af.0_1625746324367_0.38726476838485246"},"_hasShrinkwrap":false},"3.3.2":{"name":"@sberdevices/assistant-client","version":"3.3.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"643d4fceaa6a3dcacf4ceeea50ab04c5d871e747","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.2","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Un4ynVLnFuyxRdTnEzbQj26cxttd7Y002lfQn9vwgY81KggGJ9hpBmP9G4qvgrAItuvk13mUnkOeq3s2hkLsDQ==","shasum":"7364d4f057971864249ad85e86a11a45dd408819","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.2.tgz","fileCount":66,"unpackedSize":1001773,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg5uzcCRA9TVsSAnZWagAAlL0QAJ+UTe7ISKXr/0Accf9g\nEnxV2wRDVxZMoixfNA1ktyQJfhtyPr5QcBQqd8MqXrCnjTEjlViHZkXAXRBX\nXlBUOszt48Ve6KLb4HA2ZolC6Op4c1U47FWjhbYjJd3ildtaYMRVtKwHVpOc\n28UH7wGaDvsPIhh6XeeVIefQ5HgZRdQB6dIUbTaFhk0o60t9SFDrfMLkyu+T\n8d1Vc032S+OXkW1kVv3eSIdB6NgHuaqTRnKUI+YypC9TyEZczLhmXjA68WAo\nwYp8E29T7UP8GwCh3qbg5BJoteqi3wxvL7BULT65cT8Ab2ZU4q3P90nFsRqI\nr5k9jVF5jMh8bQHTXksibbyAZ90BkWLdmh+y4am9FRlMMCYP4lz2lkkqPamE\nEi+1Z87h19KkYYwKpb5c6lBk99+odub4DxVAbZl8hr7FY3aerkixDemWRkyz\nY/Avf0xzQeL3uUa31IgVvk+n0noRPx3WvFEozDX3REGlWmkiqvTXqdseOJ8F\nH1mJFJo/ZVijEnJ5FA0nOwkhTxSPZtYw2gZe+uaWuSc2LUUOnk6K34Ex7s0y\nB0Y0ZSYx5n/02/VqbjdlTimdBwXUSXw2uBjc6LxYu2xa09ZUuCczNmsYoGvb\nrAJDydxAkgxI1Z4jMIX5uKGja4VlrXdeljAlxTMSJWUuwayu+t3HS/UiyaTM\nTIjI\r\n=bi7y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGDBhGr7uAJwZSh/SDISWh0HYFrVlum8PHhW2ZOaOLKWAiEArPdcj2twTn2IFcNbJ/CmjNWEdFYk1C6LD2dzF9aWw+Q="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.2_1625746650274_0.68933778728574"},"_hasShrinkwrap":false},"3.3.3":{"name":"@sberdevices/assistant-client","version":"3.3.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"build":"rm -rf dist umd && rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/.","test":"echo \"Error: no test specified\""},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.4.5","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"4.11.0","cypress-react-unit-test":"4.14.2","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.1","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ba6a3a0bee3b89aedccc56364fd5c63ac0325a3f","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.3.3","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-ntYKrD/qp48LSEffWWuDqpsPKTs/dkaUtwLCrdqCmXLoxhSuoMEDrdLmmpaE37z0hf+qUmASVqTtN2vO0GB2LQ==","shasum":"046508b0b229a8dfa645e9277b979df1dc6e4639","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.3.3.tgz","fileCount":66,"unpackedSize":1003273,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg584jCRA9TVsSAnZWagAAxmgP/jmyrHN3yuTOO8Y3rk+g\nkqhBYMxnRE6PscKzqBKXKtUEmYllqQzWSPd5aADUUnEFfxVMqOn7I5w2zYLM\nlTIXLTinOZE/2zXyRiFo7iLJECpkY0W1kxZbjWi7/jg97MaOLHq6Vf8Cz+rd\nY1ao9TNILRvD0u9ZfFHb1dMWjyOdCiRjoJSFtx5GaSm6buF2wEtQlG1GaGjx\nWW7lnhFpNSbNTomHSHQsZlEd75M8l9ijZEa042HioiqO2c5pVBF/yQrmCRWi\nfw7MY+CGekuBUCwS8yee721ucfB4kfFHyEg62aHk+U+d0PzNpZQaHrh4thv1\n20YV2olXz1N5S+a0n1nI0jzubsMi11TocCXr2Vf22ekEgAjzyhM2s6k2ELxa\n6vj8gc+Q/izWWvKLm24fOqkX9Oq4fN9UCM6kJn2hU947pK04il4oDUnu1Uzy\nV4lvjXigJ7f/1wfU1ddU/jXA5LwCnV+6Ag8pP61WjywJRJYY9HvhRYbaB+f5\nqdbKbrtspSHMSkJc7b7ZnnItt+GP9Xndr6h2ao0FsPaZ975ox5cxQD78zDX1\nHCTtpR+e2OdPAuWjiRfKTtTOumTy66bruMzm+J9/zroQODa56Chw0ODVw13m\nslErqjPA1g3VflghcT/ousAx9DQ5sI7SRWsGF9xWebaoN+JuqmATif5CN6jR\nw4JI\r\n=xy6e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFbRdJMagycx+twILj0hyVmlZiUJNe1cQ6HgPM35pzgSAiEAob/kwS131XGbySARVlsYLETClLHJBD4+5KehShnSjvs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.3.3_1625804323135_0.10182651645552787"},"_hasShrinkwrap":false},"3.4.0--canary.152.b82a162fc4f75463d6fd350be0150f83f4eb99b7.0":{"name":"@sberdevices/assistant-client","version":"3.4.0--canary.152.b82a162fc4f75463d6fd350be0150f83f4eb99b7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b82a162fc4f75463d6fd350be0150f83f4eb99b7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.0--canary.152.b82a162fc4f75463d6fd350be0150f83f4eb99b7.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-4PaVHVXXaT3HKMAZzhNfeE+MPhraDMSvfYRq6/bQOfKdvfUK6ZZkpvjaWAsTU2mcel3WugGnWAYIQX6rMbiCmQ==","shasum":"7b77a8e2adb3b6cfe6ef5f5d666591e57a5ce601","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.0--canary.152.b82a162fc4f75463d6fd350be0150f83f4eb99b7.0.tgz","fileCount":64,"unpackedSize":1002208,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg6AUGCRA9TVsSAnZWagAAZoYP/iYO94Z29gnC5bVKpq1S\nWH6QaHZVTcPVE9aNWOprkJMoe8vjEUEnyLwWZztnJsVIFi5z39CMXL9fYq9S\n9NVFh6Ipu+JSXu7CO3XfhyVYGe8LkF3AZEI6ISldG7PB6DG0n5UIS2DTkfik\nBZWMqobqP4uQs0+b6gFDd6cJv3rGBM3KZsIdPdVtdv9Ri1Ee4MEHEdV3IdXl\n1xosgFPkeBnl6eK9gfp6UgDaPv/FmsOADwZpNzCvbdpy8RR6PWiXeFafh5b8\nLq9iJBQ5pyQibfAPiRX7QBnvApSeG0g+BkC1p/4gnh+7O8T70WMxo5olRO1J\ndgQkYph1inbTP7x/6eIbKq2WdstrzGWOFkdW6YEmhlMQotGGM/g8tMOKFM/z\nDNpzeN9Y4Gzroa7Lp4APDybbb7WDbZvcUuK+w4CHr1wRMB8Cr57mfB5jjB5a\nVLFIarSh8OMh3UmRQpAF2MaCTnenZkwlQ6HqLdUlTKxpQbouGp22PvbWkodZ\n+OeHkGJ37isx3rCda7YJLnrZC6wn18AtqZMFGxgYn3SqmCCxKGXAYndtmdKO\nvAEjF/LoZuawwmZWtpZqEJ8v3SYTICqd7CD0+XbxRNmnOvg84AmO6rs2V1Wo\n/4mU9L7BDT9CaGoSxqmb9eYSIHYhZVUf1XhuYlLpuO0AnApL91vD8U0kFm2+\n95lo\r\n=s7fN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDczsigDcAXXMV2pT20myjI9goV+5GCnf8TDOcHn02BpAIhAPj3Td+U9ZV0/QqTValP70jgtFKN3/qRvCqSZWAjP39A"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.0--canary.152.b82a162fc4f75463d6fd350be0150f83f4eb99b7.0_1625818373640_0.39379613982177"},"_hasShrinkwrap":false},"3.4.0":{"name":"@sberdevices/assistant-client","version":"3.4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"356812b1c4a9c24c85b34ec9880a38cadd791f3a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-6QTxI+4xfM+umoJev3OpxaWlrE5HXiyUFlOPtfkinouVKsJkKJlj9uLURX2fzwHQ6+d+iOWj+220t0gxgPchcQ==","shasum":"3cde6bdd51726ff19ff2dfcbd32b5542ff43d091","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.0.tgz","fileCount":64,"unpackedSize":1002632,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg6+hfCRA9TVsSAnZWagAAnh4P/j386tunoTSpvYp6m4aa\nIfJ91VBULN/tjdeErAPZ0NLmVR4Ifeq8bj3zI9YgO9WV+A8KJ5Un2eH1r7Rv\nLqbM40Tbl1P9/xINoMYR9Shjn/KoGiX2N6m58KIks/x+SuVk1C0fhfI8KASl\nO+Aj88NTF58Bu/kRxtTmWPO7UYqQysqYXDKp9a8h8J1VMOwhitxBbjlonkGJ\nM31tOPl1v+SLrWdC3/+SuDv0tknFOf7Ns8tPn0kJGgQFdd+FsAGzzH7I4DNx\nVN/wKsfPlYXmGwel8cnajZepTaXYc8siM+tR5FCYlZMv+GbLbFnA/xDiDkZk\ne3espo8aphu5mdmEmalRf8HpUHaA9gv3HtRefTbaT3ByrcJT4fQR2e0+TNXM\niKXHQ2cdi7XtZHVOfs7eawzX5WfMnqpI2izsZQ6FCidBrMAZXKpD4DfyncfU\nLC5D5K94rqlCKsqk1twPNaClaVRznGS8hllfmhJKuaSNQUjOPWGY0i5K1w61\nAkO644msLxAguh438kH0gT1yqWBw9hcrCDjMP2vl+FlxW7bMGGg+Qz+90Fez\nIspGhY8UImwfY447TVmy29eQ9KJrpn3QPjSLXyvODcYAXWTldySDrCwZZb8y\nLfz4JmyfT2BVqseWFH9OBNwJl3PkYULjTiKWfJIHjPK+TI3S1JBDlMM+KIen\nJplK\r\n=r2yD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIErcXL+9Q0DYYc+dc3NUb5/Rj5CiG3xTL7O3VPAYv4xyAiApkEuG5Joba8HYR3hl6WZDa7np1ZxtNXgZFv271xq23w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.0_1626073183040_0.027813512704637944"},"_hasShrinkwrap":false},"3.4.1--canary.154.33321e71609b360e4bcce573f444b249367082a6.0":{"name":"@sberdevices/assistant-client","version":"3.4.1--canary.154.33321e71609b360e4bcce573f444b249367082a6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"33321e71609b360e4bcce573f444b249367082a6","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.1--canary.154.33321e71609b360e4bcce573f444b249367082a6.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-5/nMfzBtnDjy6QRXkVLCjTZ+C4IuJOwJLZJnAdFlBwfnkIS2ufp/Er5uBIUVqa3qXAhPzNPSxAZqSz9TySv9wQ==","shasum":"38b6d71aaa3f474a34bf406b3ab9b9f0b57682ab","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.1--canary.154.33321e71609b360e4bcce573f444b249367082a6.0.tgz","fileCount":64,"unpackedSize":1002810,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg7DZdCRA9TVsSAnZWagAAFX8P/jydu/3FD10Jf9sCzQ3N\nkOkTq77aMnZqKVCqi+BT7BV+ix+Naa+bDcYSCquVX38RtX0c6m8zSuo5UeaM\nxzCO71Px7eeFg7gwXFKEJzIueWu5H4ch4RJvG+zbn/NVWm0un57MarAv2ZVQ\nynhSAeGY8xTPFF1q1ppjumjaAZ1BQr24Rbev1ABT2z2ZLzcRZSkyl18Cqfxc\njAqUCQLU7zAbbso2aJlosuzq+P79FqHzWtfUalZ1nO5Pe7nijlEbXrC8SzwK\nx240BwO39I4Ljd4CjNpYdEKAfZiGlpN8YC3P5FR/VRO93D8w9Ozy3hgADmJX\nUrcb4/rOzJXhzWrvO2o9bNFFXxmRBsoz4erijpqX5gU+EwGUuFKL8MIm0UJ3\nnYGuJ0SAqmVx8AdBvqtN6BHKZeeKsRlUy6kWJomDJHjfujlFPVUok9fB8Mpp\nU8C2QHslbS1Gk7lDwOhi4ZOHNDLMK1x8T/SAmvZiN9IXpPyW5Nkjkaydco5w\nq2eAcvvMmBvbj8+emzPHCZHvaYBlOogS1gTxpiWxEz0KuuRZaB4A245Zn64c\nY0uj9n7AMmnpZ+lOcnRl93g6h27SQBxZFmwNq9AbvQritPDK3793s7JVW24t\nVLNCICvupIfFstrBNOgZV8PfsABYSWukqs2f6EHCakMxWj5AriH96xklqIco\npVMn\r\n=ZoSa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEPGPO1/pcgSuIwo+CPtLEYJVHorW325pZCPwMEvlY+SAiB8L/W4iMEB8xXkqC7PRjQlNwRoZCrvTtLzWovbASeURw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.1--canary.154.33321e71609b360e4bcce573f444b249367082a6.0_1626093148962_0.717095628090022"},"_hasShrinkwrap":false},"3.4.1":{"name":"@sberdevices/assistant-client","version":"3.4.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cb1cbf5b321c16f8a8eea70dda1f45f41567a348","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.1","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-18gXDpNgFhpm+hlwhdsUoza4k6gLGdqIOGxm284l2dMblTzQNNO5MOkuk1psanNPfQRscnlPu6olGiNrQr1kog==","shasum":"a83e024eb36263ce3225c321baced7f2d1e1f7c7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.1.tgz","fileCount":64,"unpackedSize":1002966,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg7Dh8CRA9TVsSAnZWagAADeAP/0WejNIpETs6cTNk61R1\n+wQCmV8lRheg+pfYhkXt/lCcknmjodra5uBJX/wwS1pQDyJ0JqAZo7ppm9S4\nVjudNl5M3VkVsnQtSGEKB2rrZwJIuj1f/SjnoEk3EKMsjuD3lhZFSuiYUnlj\nzoDnIZV47Vfh09nbZt/D6zSeoQndyRcDMX3A6Yf8XvMy9omx2qulTuslozT8\nfRTyqyli4DgPHtd0HNWvIKuvfN6ynLi6qe6JQpO+Fg2xYgN1Vzoir7tOArnx\ncAy65K5lvRuMvlXse9ehy/R/Ygt/oFL60HBASuqcfiyoLpAE0pxhK8lgEpI7\nCO36g4twLmlzkYMk68xmMJz+YcrqB0FjrXpKxFxwobRHsEJziv19NbBIt1kG\nf1H6BRpsQ+iWLLZoNs+vTcg6tFOnObVTawwWPMr61mh0Do9c9dj8rVcPbnm4\n1bL2m8FbhFiIqDwPDsTuX5vbovr2eOgl9f0qqiouebSShZ8kvpmbDVH5w1AW\nJgajCFL8nOAmcWzrSS1Bj/CysuLkTdv3qQy0miE8sxKpnw5JtuOBqYu9tfLg\nx5GmOWMYxB66ZXmyl5w2dbXQLz5e1tjCMzn1ncJbqt99V0dWMeRV3woXWMCS\nCAv/ZNloTG9yTh+g41E0NGJileRJnLLJID74QewjRaMkSMNN4+/vDXXsh/WR\n/msK\r\n=8FOp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHxhysCQYXqvcMvnwPg8bHQAfQBHe6qAPoGgp0jC+1WgIgXci40XKz11PVjkjcRw3tyIjL5wiwU+Z3BgcPGIV7MS0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.1_1626093691980_0.21753735888281067"},"_hasShrinkwrap":false},"3.4.2--canary.155.4bb896416936db16f275cf9145f98bd8056f4130.0":{"name":"@sberdevices/assistant-client","version":"3.4.2--canary.155.4bb896416936db16f275cf9145f98bd8056f4130.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4bb896416936db16f275cf9145f98bd8056f4130","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.2--canary.155.4bb896416936db16f275cf9145f98bd8056f4130.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-RUJzBDu+h6dvyW+FMhQ+OTb2yYM8ztpx6E95j6tALc4Tzn5kX9ftoX9ryrp/heH930JRrv4J+f2dLZ+ATOyGqQ==","shasum":"534513e90db27470cbee7b4ffbbe4c3baa8060db","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.2--canary.155.4bb896416936db16f275cf9145f98bd8056f4130.0.tgz","fileCount":64,"unpackedSize":1003628,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg7VHkCRA9TVsSAnZWagAA0/AP/19p49VRPphyE3ywBuMJ\nC4/vYbTlNvEaDRZ+wML5S11gfCfapbK9n/CvlfX6Rdjl2YDCp+Y2WNEDVc69\nc3xmR+MPgbFbMAO247o7iwBRZM6cLSEifbCqajQTmxcbdrFDMExWrFxX4X50\nMTdk+K+B2MX5ll4qBUoprEhzneeXK0QV0YwDPnJzHLcOzMs6P+QlLaHBM+N8\nUplToLIXz3gQhqrtbtBXQ3H4hQcwPl47EnlPAjG0Pf1kqBv1y0FVvU0lLqoH\nk5a9Fw5k6ppT7IV5Mu8V54alV6PZLzymtBeHOt8gHZF8Y4lb4G0emfD0o7Dx\nYAH4PLEEy5QSw0AHYjiyNkXdHXL/SG4+2udA4vBZhzw0cfDsy7sdHWWBY/Ec\nx5Iv3AB/J5QuWKEovH5kp1Ns8oATlfmYN2pWdUirYmtxLKN+H2rfA2CYK6f2\nwBGEVS4ngNMvghKZS59+ckWnxG4G/HFGZBh3CFi6nIchOFzXNFYM6tOI+L3C\n3m3ZyGHtP0ZzCFHzyX829vrLiuaHQdmIXsg9i8MBvwHPUpaeQtuhSpGxpDNV\nylHdB2TI0wKYfbV7frcmoMvXRymqp3JTEmo39wwH2wd8soKZPGamFK0nMjnI\nLC0rZd8eR+5Qxx8Q5j7O5CPfwmH8IOrclELr2eoovvQ0UIYu7+tVIxHkkPee\nNY/u\r\n=SlFi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBqn0XvkISTwpwYwKiaFaP9alrL76pdqSXUU0pnLCqtRAiEAhg4M9B+kznZcp39nS4fty5/NBwKch3FznMsfE2Mvk3k="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.2--canary.155.4bb896416936db16f275cf9145f98bd8056f4130.0_1626165732407_0.11640553815096122"},"_hasShrinkwrap":false},"3.4.2--canary.155.a756767b28b4b7822ec4c25d33c1651d4eac1b10.0":{"name":"@sberdevices/assistant-client","version":"3.4.2--canary.155.a756767b28b4b7822ec4c25d33c1651d4eac1b10.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a756767b28b4b7822ec4c25d33c1651d4eac1b10","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.2--canary.155.a756767b28b4b7822ec4c25d33c1651d4eac1b10.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-e3+NuYL5XBg3gscrKyJjqIjYvOLdV34OGLwfKmylyJrhJptWdCXKSCib00qeGlXugUfy3dUWM0IqA2SlVaAbBQ==","shasum":"48da0d35602f1c1497ca5518dfdbc769d2851ed1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.2--canary.155.a756767b28b4b7822ec4c25d33c1651d4eac1b10.0.tgz","fileCount":64,"unpackedSize":1003526,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg7VKNCRA9TVsSAnZWagAA87AP/1zAktJzhWhxr4V7pdS+\nhBzFq2W2I9QzAiTIF/pV1YXkL4BmVjha6tOYVHi3Y0Alcv4OVk+sSDNJn0nd\niaT2+QbdA2O2sAur0YUm1VZb8IwxKBz5aNscut+ipn6jUp4LkV32fs3K+uY/\nGTlq4lVu5ey2nmGdJG1R0GmmnT2jW1/WP2+MFjNt7DlYjn9yQEoo2gFMIt5Z\ndRyFmBQ0shHFpLAgFTeHOiqIh14t+eD7ncjHPZkU54piWh6hI+y+IcNPcwIC\n1MQllLk1QlKRA3zU/EGk2D5V/B60KgUlz7UKSuxw/8ACzUnwYorK+mPXPRte\n43kCIVfHFbiD5aj2jWWM1scPEAUcHj7r3F2De7p7SqUve67vdRXlCWkXLvDw\nSuRdySNkwbs5hNSMHLO4tOifAxoGaEavpqGr0XNeoDJwg2pisyovvv9PYqcE\nAHxYILNqC4ksiVzrfb4a+G4Vpvgkxd7oihCeJtZGTK1ru8d0EaYlDpXqBPM5\n9M721AqqK78gRiYQtLWtC2MwN0E1iyK7nQqwnscKhrOwPKxNjU83CZiM0u08\nX4l/4XZ8726E2PusVo1eaTTHmRR07ka/d1euJfRlRzT1Ex6jthVA4N2RyTT+\nDZNJpWRb4+q9l2cyuneRmkbSi3kfCpOE6M2lH0PvUbPy2f+FyLVgQgcB0jLW\neM05\r\n=d5fg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHGpdsus0KXVdC314vZdxEa22/nz2PnPGg6gBkeRIRPTAiAzDW6GzQexYfaXUbo9GqpIfxXw4zTkdbe2Ef+7rhj6Jg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.2--canary.155.a756767b28b4b7822ec4c25d33c1651d4eac1b10.0_1626165901179_0.6106886200232362"},"_hasShrinkwrap":false},"3.4.2--canary.155.8086cc0b6644203419275a7e525a539e49575f86.0":{"name":"@sberdevices/assistant-client","version":"3.4.2--canary.155.8086cc0b6644203419275a7e525a539e49575f86.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8086cc0b6644203419275a7e525a539e49575f86","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.2--canary.155.8086cc0b6644203419275a7e525a539e49575f86.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-HEP6OZduQoMB0dG116EAsv5UGqhc1YyLqKAl4NYorncqIVcB3fnxu6bd70qmur/4+7ZqtLhROtpO5ICfIG0w+Q==","shasum":"139b05ed9e2a3a7f0c474e176b7ed1c311c1724a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.2--canary.155.8086cc0b6644203419275a7e525a539e49575f86.0.tgz","fileCount":64,"unpackedSize":1003036,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg7VLyCRA9TVsSAnZWagAABRcP/3ybxh80V9FJTBtIgcN7\n+dc3a/qMZUzqTjCokcKf8DPtxq9FqwEYgwTT+0lNjTYMtAXQQNynHy1bHC2r\nWSJitcmhhbBiigiccPMFWq336vzNHP1T4dALJdQTyiUaESvT41D5tCX7TWWg\nZ28OjIDhut7CtBzfm4o0k8jmY1imOGcgNuNapxtgS/YHNu8IQIt7iK3TzZN7\n7jN+rCAE2kII+vBKKxn8qQczZsSsav1D2NhS4pitUG9JFKeZGbAceypCXHio\n84dX/QuJgWmFIGVKSNgFg+Fr19MtbLE8MrrHOCZKRF87IgIMVCcjpRR8w0ns\nspGNSJVhgdPU842h+qzW13EPNQDbt71zXvNzoiBQfLGaJpV3oNraz73TcA9s\npjENeRtfkWL5PHTsaMf6MnAfihXn+3iGSBBA3NH1KYiq1hUgphHDbmXtEe8h\n8lsebtes7A8HU07lPfvGB4SdV7W0y+YHNEMa7yIdaYzdFgshvtWL/D/fpwP0\nZsstkbFokxe0/JWh2oGOOAdZgi7QenQJeP8QkA1nDtt+F4Wxu50UCHkB8HVN\nhCuL8yxEjdFrW1ea8W6+Cg9EsE2JZ6gANaPJGsTETLgZeAB+AiPwbusYTuxW\ndyTozhGyZQZwfuonowj5dNRDHmVYciikRxU3+RVn+ITUBK46NGgKF5VXLz3L\nYas0\r\n=0ual\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCHNfTxJI8kI1Baw3PQ1IDI+4CsZC8DgENmvNTvG6tl9gIhAImjJacW8xNea5KjEBscIxxfehRvXgXh+IJXK83Ta0qS"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.2--canary.155.8086cc0b6644203419275a7e525a539e49575f86.0_1626166001978_0.4231961386707439"},"_hasShrinkwrap":false},"3.4.2--canary.155.18b67de8e49be098b99dd7b01ef307ce0a961db7.0":{"name":"@sberdevices/assistant-client","version":"3.4.2--canary.155.18b67de8e49be098b99dd7b01ef307ce0a961db7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"18b67de8e49be098b99dd7b01ef307ce0a961db7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.2--canary.155.18b67de8e49be098b99dd7b01ef307ce0a961db7.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-V1bFfpXvlvIGrr605g3Bf6wHhEDa2Pne4elPQAAcsAmVzTEC25A4b/22REK2yg1Arez8gOoNak1vu+uIpEPX2w==","shasum":"fe72eb0f5d039587e4894f18381ede61468c889f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.2--canary.155.18b67de8e49be098b99dd7b01ef307ce0a961db7.0.tgz","fileCount":64,"unpackedSize":1003116,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg7VMFCRA9TVsSAnZWagAATUUQAJS49rku6SLvLEWjicXq\nQxTK6WOnH9A27GDnjA+rIH8o3cmdut+fIB3y6zk9KCH20vRcONzTREJzqgGE\n0gcL5s0N3rIYRomkb8IcG/OLt1Vxf8q1/x1r7zK3/VeAreEklfuZdkmOvcu8\nGXOxjPV/IHRSQDR6ZrKM/Bd3Cb1VUi/PC8MGk82hf3j5SZC8pcJcN93NyjyY\namZ0X6BWweSFytWFXXodRXGdKt1ruapl77RRF2tT30JPGo6fXOFpBJjsvAt3\niOel3xrKO0ndqGUKdMr9yHWXq7ZtIMkHSg5KKTxUnQud44zHaPkTdANSXjA7\n/CDSetdQkYBMH2lNY2fYBi82bIfvmSfXfCOz5my9RcHfbnHEtsjdToiXqnxJ\n+4MVKQA9UgaslhK9HpgIGt/o4oo0BdjQAgTx9ADa3T+/GoI16qwdotKkYVDp\nVKhd4oSlptZUoEAD+Sdtc6+KWUyX1HBCLZWoS6G/ofeuVU3qNnoC8Xf0eiiG\nXE74nKZnxBNoZlqVYs71ydLe/wQszYHhUHZ3dWn6jhgSoRidiqawLSjOL1Jz\nQPrTO1ruLIg9nJ/spgZulEeOK1cLREIdYnR/ZoO7JacQQJ09q+ZX8DuHQdiC\nkQGsgOaCkkLG58yLTbwZKJGQo/6qiTHxlk1NEkcrobU71AJebTkpJmfEE1O0\nahAS\r\n=H/xa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCqnmtOjE+BP8M5U3dKFMIeivIGBxC6d4Vh4+YoEgUdDQIgUmYkUtL0MnowujuVoSUjvE/nSqeTUJFjDokTMGBKo6s="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.2--canary.155.18b67de8e49be098b99dd7b01ef307ce0a961db7.0_1626166021030_0.6253049691025323"},"_hasShrinkwrap":false},"3.4.2--canary.155.a15369ff82e8a7d3aa646dc730e09909bf38ddc3.0":{"name":"@sberdevices/assistant-client","version":"3.4.2--canary.155.a15369ff82e8a7d3aa646dc730e09909bf38ddc3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a15369ff82e8a7d3aa646dc730e09909bf38ddc3","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.2--canary.155.a15369ff82e8a7d3aa646dc730e09909bf38ddc3.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-pzgyOrbv1OdmzmdWqpe+wUvAP9CJJNd+AXkMOtyK13/30ks+TisX2LVNzilTe0aUaw40scxRoUvb5v7mQSKvWA==","shasum":"72a6f14ce940ed0ec5f0643c12380f3dffef8893","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.2--canary.155.a15369ff82e8a7d3aa646dc730e09909bf38ddc3.0.tgz","fileCount":64,"unpackedSize":1003099,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg7XSkCRA9TVsSAnZWagAAiyYP+QHSuCDst1cJsojn6wmH\nS/zEmbgxBzFL00/eW7tDIbhCWGpmwnO0zN2cjdlnOTpzj5jVw+ZnyZ9GC6my\nxPvgrSGJ/dBldN1P1B7AcEYyHAJgtqCbAV2XDT/TutGuR8i8uroppdb0CLTp\n3JHrnKV21nFYd4j22MXojc0MhSpFmDGCEUB1lgMT3Dp11sIiChyaF4Es4kQp\nwhMpEtMxpJErDaMq+tdm3vyyfrnFnwxXnngHlzJwjj5ixti7cvczRbtugnih\nM+voHE+piUK9bQxk6WmoEpFmtzRgMGUBlUXpNrE0PawMAybo/P++QoM3boNf\nQA9OfMGjAi8FEuOfsHpGmzssJJYuxqV4sxQOyRv3hpr3whuxi+9UjqT6dTej\nrbCzg8jSW/4V5mFX0eCfIlJw3KxBL6NqDPSR9vg9iYxC+P+e1tgZmWUIKYkk\nJFAcHWdKyxS5j80HQbFHhRsggTJ4Beh3+Ux8s5lSRgran6giVvOFGg+aBd/A\nWAFUs6Q58HE6jZJqhdN6xcWT5UiCwUsxSUaN5/K+cgUB0qRj4A5RG4zcf5kt\nYgIKSptoamNZndwkDSKaoyBPqAkF4P9ujAIp4wMcGeDnJ96ILmqE1WLNWJAz\nC4Whyrn9VVxhn79XW8U0Q9V6/2RXOP8lfwIwmqATNyC9B8ghmguW+3Ep3i0+\nDgrU\r\n=sOfy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC84pmFe3bXt1pZdQQUgMT1skp7Amj2kya+ZSIjX/XkiAiEApT/ku6XUyyJRuJgSi2geZZQpfN9vOckk0aLaz4UrWZ8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.2--canary.155.a15369ff82e8a7d3aa646dc730e09909bf38ddc3.0_1626174627525_0.7674443075225603"},"_hasShrinkwrap":false},"3.4.2":{"name":"@sberdevices/assistant-client","version":"3.4.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"81a516ececfc562ff98b3fadf0531a95f78f5369","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.2","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-muPO4JauRnxn1Uh140tpI1+nLLnkNqSyqEw9G8ksY39IPs/yh67Jg2u/i06Evi8DmacxIzHnHIUQZqvBaZo0sA==","shasum":"b63719493167ee833c27a0f9b47637ec52b75853","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.2.tgz","fileCount":64,"unpackedSize":1003453,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg7p3rCRA9TVsSAnZWagAA81wP/ijtthgRO8TxzwTdV1fw\nnPxPOZViEGd57rY7LkiP1aGcGx+S1Z7AAQXJzdwZVmk95ung5dRf0wdKj4At\nNCBmKCjk6h7e5p1DTktjmOuieHjHWZHLHC4v/B0WwWdilWcF6RgofDGN7nqy\nnfS0JBUC1YbAI7RxjiD+EWQXe7MoE3Vn3EMQIZ8kPA6R1dsWKS3aSDj0IDDn\n+GQPNNL3f582M3uxtk1cXqmL7qk8F70ZpQIa3lUoHIibzcfrwfGb+ht/AnAw\nApTwrjC3o4m4ujrXCLEHFiRC6pvjEkqz/wyqXrXpIPMoO4Zfzn+VcZSnB/Gl\nsoht46C4IJT6ao8ZfZxxJW9V85ujFMyIgw7LGLdDqlonW+zHVIdTja3DOgij\nXUoeLXoRdC+Kxtpt0OuVjoYPuMXb0YY7ZZZWahRT3W4yhw6tl0j9FoJnN0wY\nsfNK6IrV0dl2UiI2pY1AeX7nKEXNA94AIn0BBjY/GJBU4zJdjLY0xehqRvu0\nC90K4zuAGUSmIb+Ia2Qi0hzYGagICp2M0VAWwAYR8Cd34qF0Zcv2vb7CDTWL\nQ+Px/uaAWBEGqtv1C1Fb25VTT5t7l9ik14BjJpzB6Tc9knKrL/6a07xcg4fp\nK2GNfIsjU2UWkWgUL/XeU1PxGoS7p20keurzmb6rao0J+EmKudjmIerGVdZ3\nqVNj\r\n=oG7O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDH8dhKCMZmPpiVTG63KJiUU1XCkk51zQCbD75pXUw+8AiBO1YFU+8OzoWnodXa9G9S7VJ3aqPPDzisg2JPZkhVD7g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.2_1626250730475_0.6722785226723957"},"_hasShrinkwrap":false},"3.4.3--canary.156.99808f9e304abc4eadac9869de16d525cd6b00f5.0":{"name":"@sberdevices/assistant-client","version":"3.4.3--canary.156.99808f9e304abc4eadac9869de16d525cd6b00f5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"99808f9e304abc4eadac9869de16d525cd6b00f5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.3--canary.156.99808f9e304abc4eadac9869de16d525cd6b00f5.0","_nodeVersion":"12.22.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-H2ng8uGgDIQh4sXbwyggqnIeONJzcQPbSVkdKI1Im30I25A56csEnGKSEs4s0M8ANYLAzAOeL/27+FQX6To9UA==","shasum":"7835692f286fcd741685af78676b2bbb9fd20a33","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.3--canary.156.99808f9e304abc4eadac9869de16d525cd6b00f5.0.tgz","fileCount":64,"unpackedSize":1004195,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg7tGNCRA9TVsSAnZWagAAaxcP/0xw8UfbhJ0ndAuxgLPh\nysz/kdZA84IaBoVPQi7dXEExmd+2HFzMltMiNh/mp37YKgMybPSwlj9Jsvxu\nncO21tqKbMqupB+SvAHsL5bJeQ7771UIKbK7YM+G2jcECzLWrjr/IvnA93cM\nfCm6UxQBhBSpOLKhSAg4Z0bMQiUcPIZrNyZ29Ah/knBTFzMr3a/lyERkEaOE\nStJC4BQx29vcopkTTwphG1d5X/QTGlPbXJbNKcme+t3KPzKtWPTs4FgkR4eZ\nHHwFfzCC/CbxIyP1fzxWbslM7TXEhDNV4qi/7gsSvD/rlRDK6eFh4j2ftL2u\nmz2ARhhD1nGEhSXAZpRsCdataBJXZ+wS14ygQkx4Sr2R/3E8AHBjshqjTiXq\ngMXXRnsmK7B0/DkU8nP8dNBlT2WxfmN9iYk2wL34YDL8he8tWQBTiQCiaM04\n3YquSKQH5K8oGpVDTsxJR07IctQR66fx7IoilTO4NUqL4ERglBSlhn2AUGSr\n9T9IJcN+fhJW/Csv1j6H0qt0zz+lS0Bx22b4y7l2MeNeBNvq2oLrCuNkq/5/\n/UrhzFCpSBcSPc16Ec9txo9DeoH+yKYW6MJK4RErngz0+vM3M1SasNHfYy9S\nghydFJkK2xumrjqVArSrbn+U9SWH/iQjyVmiPmEY+LVD1GdJX8E5w/wkDEqz\ncoqb\r\n=RMIA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHG8fsJFdZJtE45SIFLZc7Y30373vAIUI7yrzOGoc+aAIgPCXDoxJLIXqQoiePeuqfuBRdhseo702ueGenTFS3k6o="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.3--canary.156.99808f9e304abc4eadac9869de16d525cd6b00f5.0_1626263949458_0.41015790687287446"},"_hasShrinkwrap":false},"3.4.3--canary.156.ef01cd284c20fc93dd9993d63015133db1007c57.0":{"name":"@sberdevices/assistant-client","version":"3.4.3--canary.156.ef01cd284c20fc93dd9993d63015133db1007c57.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ef01cd284c20fc93dd9993d63015133db1007c57","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.3--canary.156.ef01cd284c20fc93dd9993d63015133db1007c57.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-lq+4bQFllnmwo8S8Wnv64ZrMuoWwQrIQW6u3aZet+2kMylc+InL/HbruWx5bf+aR16Oz6vsqqk+VFudXuC/gqA==","shasum":"7bbcd15f9c98ab19281682b3b7e52d729d41c885","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.3--canary.156.ef01cd284c20fc93dd9993d63015133db1007c57.0.tgz","fileCount":64,"unpackedSize":1004424,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg8VAYCRA9TVsSAnZWagAA454P/1bAr4Fm4lzL7iZnnJRY\n8AlHuAKu2lufPmZLtfvFU9pGEpMIisPp6bwiDBHxw93jkTOw96n9tKf6b5+P\nQfK6It1TqaJDgRbo9BiozKYNO0dfAZIWDQAHq84ON9/LIEwGqQl9hdQlLA+m\nmOKltFxqFSrHsea9wv2Qu/bvyxiHX2RHTYzKo13CqO9d/uenhbDFRaR/vIGG\n5wXolVBfZpSZfk9OoYefVsEl88muHCjZbL+7Kfymditrte2Dy3cs8GJvmecJ\nSN+ldwolZBLnCEZ8fYybqdgDPPtiszb4Pb6Q7196YadbEm+XNMQdWMkpgZ0P\npiXy0pv0zdeLO78Dgj129M7IgCeh5KKIhoCCvUg+lCRHF3RmQerobpWn380F\n/OLBVoQsqzHt8Tj/yOmyq86WJmFHZMb7esvS0rtGxashF6sChPvX/3GX8l5w\n5lPNlRObkWiacUuROoW8hyIV/uXJ3CmRBTshSMT/dBxCC2OfFTKhZ7aBy0A8\nMwH7EznH+wWkSEJVTdQyEJKWj3iUf3WbFcbYQ6zicW6VBNQMFLDz7zJ0Thtn\nhBjap6w7DONcYjUWVt1nPj72T1eoyk+kNiIE/Rib2JvP43d4jrJvXihlm+Dt\nKLAp9ZX7A0NOrWHrvuv4kWDhKVy58xC0e8LGV/o1osMt02xINc5vSW6Kn2Rv\n1jdL\r\n=ba94\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGIaA4/x2osaJ6YurOMoix6UfSL+IEjRUbMAqMWzbjt3AiB7egEYxDcU9Bm4CYBpQgHexlz8VSJU1TX47P6cJsYJnA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.3--canary.156.ef01cd284c20fc93dd9993d63015133db1007c57.0_1626427416649_0.8002951232167501"},"_hasShrinkwrap":false},"3.4.3":{"name":"@sberdevices/assistant-client","version":"3.4.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"66cd6e344be3a9b6a368e4cdf883eb44b61027c3","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.4.3","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-oxVj4WjjCyo679Z+P6ekZCfgV2P/1fbCs3xuathd9wMBn1nYpicTht9pXIpIJBIRA0vFfhAOD3SXf2yLxc7uhQ==","shasum":"4ef412e9dd25e2fedd5ef66b38e09450b8924bf6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.4.3.tgz","fileCount":64,"unpackedSize":1004581,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg8VXICRA9TVsSAnZWagAAv7MP/0sD7vS/9RZvHsLtQMKE\n+mhrEwv+f9GM7f+XKsJdPJv8WMV21UJS/cKwrlKYSyvfqh7RGttG7yUPbi0v\nXSyH7dxCQpaOU+WysYBF0AMz8Hq+3i29nNyJLXinImR+4xsYWsYHEE7roGqg\n7NF52Av9mQ23eOpmP58/vKSTKGfxf5hFlWVMTK8lYFxVnRszp6AmJg0+In0T\n/k+/issBovPNo9rvgqmLaUYsmps2fIlZclt8lxbuPPu/FSI5EzpeVL/4srKq\nfa5EYtQNVLzeKf//7PONTqVVxb9W64Ez9fbvYxRljTypKLANZNZukiAWHnbx\nXvJzUddh2ABE6ooIuR4QQQiM2tUNVniayB2Sy691TQHfZOleEr+s1HWC2v6X\nBctlV0zaoLIWNXIQU0iSt9fO008I1v63SageN4tTmtIie3xADQ9hqOzpmbd5\ntj/LgDv9Vv6ZqXOqOl8AqfgG8I0Hg0Y6iYIZeL6wOMg9S8RRtseI7WbxRylV\n1iH/kk6h1a5rpAQq6N/Emio9MAQdQrhlIjeH3WuxBGke0Hcy9f3rSFMaU9sB\n9YywdbcqD/cUyvPF33p7kHAm+4yICjOVS41LBoXJbbprV4hJMEkhEHPEy/J8\n0ESBJJtudDYeGb1IljXaJriOF0PQvMZNK8RvKLDZNGh3zU6JGIgtgX5hi+Ii\njz94\r\n=VK/C\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCVXTFYBPQeq8/3iI7HZIQSyns8pZesS6uGOaZPA+iRuAIhAPjOcU6NjYgJ7QHykqJ/vIFqfxpx4ijT0FP6SY0OQSMm"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.4.3_1626428872517_0.2966719863502707"},"_hasShrinkwrap":false},"3.5.0--canary.157.a82e6b66f6f10bfc6c7d56217e381f20f2e3d62b.0":{"name":"@sberdevices/assistant-client","version":"3.5.0--canary.157.a82e6b66f6f10bfc6c7d56217e381f20f2e3d62b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a82e6b66f6f10bfc6c7d56217e381f20f2e3d62b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.5.0--canary.157.a82e6b66f6f10bfc6c7d56217e381f20f2e3d62b.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-B9gAJ5D25UEOoK0jToiIdCZwuGyXrrkkauCzJ9Mq/1VMINaHu0ZvMYak6H9jiEEp0218HqLF3+6fCuSsAQPlAA==","shasum":"83a44c873253b3d3253cf3859631bb9623009a87","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.5.0--canary.157.a82e6b66f6f10bfc6c7d56217e381f20f2e3d62b.0.tgz","fileCount":64,"unpackedSize":1005638,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9T63CRA9TVsSAnZWagAABLkP/jybWgRwXNqcLpHyVYpY\nMZgKx0pagMosdX1zQzczpQAn2hQz3hnj8yFI9AxR3UaS+0DWr2T0jRNd59kl\nHGiIBKbLU2BeI7nubnehM4kxJi8tvM4fFlC95du7vDGeRRnxsoeIfwB4IZ0M\nRoKBfqFT7AsXW6SbzbAjSUqW4tx0JzTgc1i9g4DSJbbytYPXqGzPz91Lyz/s\nQcYYgqGBt2z8Itc0KEeWeSrpx8J2hDHg2W0MiQdgDv2rvqWBX4UDgQiCEyE6\nrckSg68znAPhxT/djfvKb8xjEnbqdEIN2m8NEI9lNPVnNt7ByEcYrZSWjQF1\n9BEtpp/8NN5JC7S6rf4I4SLpGzfTbetvADj+GA86sBtnMvTxoQGAS1eJh6/n\nJcUEEyfGMx6gnAnPgg/mBVWCM1kUS3sQiFKZrC9/t884fwlfQRYj75eUnUJU\nfmjh13rQ+NE3X7fzCNTw1LwUsOzzW/MU2PTyoWg76jgbYoGtFdSCXTNvwI5W\ngWuYR90Q8Lw0XGaObpDfpfDb7G4FkQbNv/m0J2HC0mbCmTFi6rmon518pUDJ\nmuWkRckCZRYMIXoDpQ64wdTOH3m5BkVm/UD3fo/xAVqUxbHyzGqXINPuWt1F\nzwmCNs0nEML7RWCpnkq/dLYsvAu9eAkfoYGwug69BXDdzqWdb5hQ9ZvToOSJ\nA4UV\r\n=EsBT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDLvEfIxdDxxrvipFyOTTHzwS5SyXZ2nhKU22U8DH1kpgIgURFMp/fljwpiVssWxNTSHiJ3S0hMsceLpHjWAtCuGiU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.5.0--canary.157.a82e6b66f6f10bfc6c7d56217e381f20f2e3d62b.0_1626685111208_0.6884207683214087"},"_hasShrinkwrap":false},"3.5.0--canary.158.df51ac9198a96599eecfe7cc10985034eeaceb2f.0":{"name":"@sberdevices/assistant-client","version":"3.5.0--canary.158.df51ac9198a96599eecfe7cc10985034eeaceb2f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"df51ac9198a96599eecfe7cc10985034eeaceb2f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.5.0--canary.158.df51ac9198a96599eecfe7cc10985034eeaceb2f.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-91kSL+WfdDd6O+/fmBUQqJCLUGF8VloPE/EKZiZ0GOgZ2xTUzIGFIhpMJvdqVRq5xHmD8HVU6gTVVOanCRSqEw==","shasum":"7b9f58ee3fe3799b72fa36e55500abf1335be777","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.5.0--canary.158.df51ac9198a96599eecfe7cc10985034eeaceb2f.0.tgz","fileCount":64,"unpackedSize":1005638,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9U16CRA9TVsSAnZWagAAANsP/1rUqXrfFrQiNnpnzvJv\nwwXC1JcXa03pxy0QkFYbyryNymV0yCeeliCtZCz5C82KipeXcK+aEqeQxmXh\nQTxqMnyw1iDzpliL1Os9DVuw78D6lyIUWQvkWsZViH2z/iAQwtdx05CTe5l4\nNUOF6HyqDf+v52ggdiB+5ujtbrhZwYF6Vzf9nXgNqI84kr2oyKdXshUk5t9x\nP2l+BHMJM9BBE+PcYZoK5dSE4p9yZPG+kG3Jx3OH1B//Ks69phSB3eeHX1hG\nrDE6pYJC6x2jkWo6eOdP6Ph7IJ9hseytC64EW2oEBoqIELpTDgATjSWsFYlA\nsTKMK01eBWwJufw6wMkWZ++ZpaD2bHelcn0vH+m8ZSPwOEOF/67DxW4uRjW1\nHJH5cPjrc1yp2Z/LT+pmCAEOOTgpeCO61MpvWP/xvBhcCYRrUBab49jKExku\nF2zKcnUYaz2z2lSK/wcpu065l5G8kBfPQfdBHJlaaWGmzhe5kKAMmL+kmMZy\n0KnFmGWZJEYgxfDJb6U09feyxJHAjbgG33riUl2UQm9TPeiKTBthlwNtE7i+\nz2ocbQQsc4l+P0dZJoruUyts8nc/NhMucRNvJjgRHO2i6tvYauicH5chael0\nBMm5NjXmqmlLXLft65vaC726ObEq0JhS4ozdr2NlxeiQpVmILxMMHlhwunQb\nxFez\r\n=yQoJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDppjRARiVJCCcTIeO+8lTLm6/L+yOpMZLYThP6Zyh+IQIgEQXpfScXwEoHXZHTbk+mpX+90JobaMFrwRLQOpEqlPE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.5.0--canary.158.df51ac9198a96599eecfe7cc10985034eeaceb2f.0_1626688890303_0.8653861375659608"},"_hasShrinkwrap":false},"3.5.0":{"name":"@sberdevices/assistant-client","version":"3.5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"750144a8088cf9a502f71f846f0ca089024fd9bb","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.5.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-HuGT7tkjNrj0kcEg6+l7XprjI6+5uenbzOWpYkto2xd1uKWeRwLAeSPnxbTuRvbVhu8h6AGRLy8Gjg5IlmWKlg==","shasum":"fb499d2e7194dca1735d3b87d818e1d5c54ec046","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.5.0.tgz","fileCount":64,"unpackedSize":1005768,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9U8dCRA9TVsSAnZWagAAAG8QAIhcu28hnWrofjjBbpBN\nS8/QsdK2tjyDLUdN4TwEbIu0LvvJ3yL2G4QT7hKqAXfJ8PgHtAdnaqabvs+X\nH3RFLYOGLWnzkqtDI4jGBvPoKz2miOYL99Od+GoQQUi8XvBJn93TCtgRNMts\nlekX+ctTM+hwVSDy/WunjPGvzIW5FS5gEjZcxyOnnT5n0UJoCXoEs1orts/b\nBHc+e1Rb1CQzy40BYtQZebwWs1uXQ9sHOK/6/Jema8R156g4yfWkYCqUPftT\nOk3hBnwODBMDQ2GI6jC8Xz+cGipnhjG8CAu8Qm28WuLn+oH97hYVKwnH7Vxa\niB+KwKgVbCqWhMmOACa7Hb+iaDMg85pmWH9lk0AZKydhQVgjn/qxM0tkA93B\ndQOYB6ZCmFwFbppWZGR9waYQFWbKOQgtWM+4nhkwexzxLF1vrXfvN6yU6UAq\n+8gprgr9HM7GdoPsUo1TeEE/h9SD3CKAJ1N5+SxGFn7QbiBHRibZl6I56Jz+\nq5r3e7FpNEE0TKqRF8nFMYvihYqrD7EpGq69TjDYVYZgWPAgGovu+15EN+dL\nHDtYAS04WyqaAGmIAUoldN8PJIQh/51QoVQFKJ7OCd8FfHcH/Xdf0Q+WVdBz\n7552aZFMqFNn7dwRqlSn7G+xwu3m2wZMs1Qd2xgMJ455n0iUq7ENBd7dGSc8\nyLtt\r\n=C0Wp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCuqwMfQYqUjLN/j6sHXYZ380hgnmSabYPfIX1Ycp8AswIgQp7zvurbDbwUVz5RSYVkfjA/b+840tZgx9G4HTDzWMY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.5.0_1626689309424_0.6109533419342015"},"_hasShrinkwrap":false},"3.6.0":{"name":"@sberdevices/assistant-client","version":"3.6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c4d1883758f60fd3e677c1e0e642094d0d62d3ed","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.6.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-wB8urydr4Tv+rWRKErPx+cvv5llfF8KmBC4q3ciIavkV7iowxiCDgNXdHgPfGEdTdYa+hzC42XLslVI35bt4QA==","shasum":"f5e5585e88c29b85e213beeb31f70e232c7332e9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.6.0.tgz","fileCount":64,"unpackedSize":1005768,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9VJECRA9TVsSAnZWagAAiooP/2p+As36y7XrQ9k4AkIF\nPrKiFftYsmxgLVPIHDjVR5hwGVRFsTVgJw1Y7uyrw+TUaZYn5DlkW/yVPI6f\nWeOpAWqe73LMXxG6dQMXbzv3kCFDulMiyWWk5BmS+Xb5zW8CNv27dhk5s3X8\nLfrVE99ZlvTXDmp4+6p6Icvns05IK6Wi2jBHIxSOKHsmS0R5E5/ccUvpPz84\nPaBKProMaDz4X6+3rRiDQlEx7neZ3ZSiaqUcmyuXDwJG1eBeaGWiBJBf1ZaV\nDylEEdjE/39o59C15ZeUpx3sN2kleqPecKIa0BBZU1qpklnsWig/kqxsHsUI\n3o+kzLhc9vFNMdU30ASvLN/h0TdcqU/xBG8uduUBulenGjewpiJuDG9LZOmM\nsejsrFNuvxNkh4/ZqEZ9FMDM3fWJbZi/makRG6vPlpWg7dD/D0Y31R2d26oU\noR5ykT8sX5F4L6pw7liugKqKA8w3qh2QbViKBM4+fz7+UZkozbcDtBDSEYMt\nad5Rh5Gu0cMiLKd7OI8vAhd0NfaKXVTGRILPablBL/7Nznf5To0DJiyic7Zy\ndzABpaqR0O4md2zyafdUSiKTK95274QFyHzPNPPy/dhppFue9byCt40HVDPL\n5I+aX0iGEJNHmvSoAcJ5yFUVS/AuehyRmX5/6e6zIF5FyE8Q1i1bCSsdtqju\n+k0D\r\n=qL40\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAqmg659KMvZH2jPVk7w9vuEZJ+fy5/vktJiAVKCW18SAiADMIEsCwAYZFbljhO9AT9iACfzFO+j+JlxrMMQBDg2AA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.6.0_1626690116782_0.532023693113076"},"_hasShrinkwrap":false},"3.7.0":{"name":"@sberdevices/assistant-client","version":"3.7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e2dd43e1e3446d799517fe398576a680d0ec8e41","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-GuZjRc9xKyxJepN8Tlj2Gh0xu9yDhLlmG7/1+o5UFIiFfMre8RGynpCm5qavfQrlfAURSk2gqb7cwXY9W0Zv0g==","shasum":"12fa5f69fefa74bb6eb011da185965b45602da0e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.0.tgz","fileCount":64,"unpackedSize":1005783,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9W23CRA9TVsSAnZWagAAgZsP/2AEjpgFOom8pAWB/Na+\nBuZ5RmXF7LVi9HMvYMr9VVwOXXvb7URWjL+/gZON5eYNu14GPeD3ehKJ/95w\ncgsK/dUy50lMy/3Z+4MzQPODo/NiML0vHqI10UAeorhEBwvWeDc98tPplwen\nm/+ek91q+r8oKtOr7Nl8qgt7sRpVLb5yVY6qJC9R0kfZ/Q5F2JzrfWhO5/rN\n1Qhk/mK2WVjMwWbac2AHlLexq89dfTzOZczoxbZ6wIIOaHhnP8C/IfEx+yV0\np9tpQ5yBxukLB8s+EphPSOu0dVWd3DLDSNyG0dl78Hf30r+hhCDNkCTPSpV8\n0X5CuX6ILhTvkC0cHWVZ+DDpchdwkt7G2hl30wuEnDLQ03Fk/WriNDxDP3P8\nWAwjvsUu4r7euvALy6NuL6l1ViSb+v2WlbDSC6G60flZmfKDBt+JsQ6yk+H1\nVte3JLRTJNarjbG0gKm8mW4+jwJUs/IR79ED9DnJgQ9TbwLbu7Ezk9Aq6ZRm\nuXKoP+NN71UyBdBdnj15OfNSJwm8AYuj1W+50ulb3h33HwMEXxhVM08pDanF\nttpZWQhMG299VpbaifxBWozz3Q7IsY30bg2GAW56FeHPFJfHpgm9xC5mHQ8D\nPYSaB97JkT3w/PsEvXL4qlGlU5J5CB40UIsPdGMT2RkiVRjG04B/a6hosN7l\n3iSb\r\n=RjuK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDkTDgONwdH2c+a7GfllIlYaRiVd/hZIfZO0Yc1aHhnYQIhAOfkLrPxTN5tI9ISC5MuVFVPEWo2cLiK0VZJwg76y+Nw"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.0_1626697143501_0.981251498834113"},"_hasShrinkwrap":false},"3.7.1--canary.159.c35f7fcb93895cfe42c81808341810bf25382fb2.0":{"name":"@sberdevices/assistant-client","version":"3.7.1--canary.159.c35f7fcb93895cfe42c81808341810bf25382fb2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c35f7fcb93895cfe42c81808341810bf25382fb2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.1--canary.159.c35f7fcb93895cfe42c81808341810bf25382fb2.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-GhjK56Ng2snynaMPbaurYguKGanhNqMs0FLeHusUMzhSOqdHQGbNp9B0508uaYf7VkvRKhw+JUp618zVYw0gYw==","shasum":"6057a2f0437797788d93614a544cad12790dc0b9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.1--canary.159.c35f7fcb93895cfe42c81808341810bf25382fb2.0.tgz","fileCount":64,"unpackedSize":1006146,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9YOsCRA9TVsSAnZWagAA9bIP/ArsfMHr+BSuOxVOqXws\nLNycVVxb+g7elCzfr0Z284W7xFCR3n6KEq/IzYZLAXBizcATyzKkFm8ustLR\n/E8ywQ1dwJ6xGst/DDehvgcKd+YgAMX+tM81UZ7K5xrXSwL1OXPdFZC+k8KK\nkX5QLoHBqIZiKu3c4ITX1HcGWWmAcVd2vMaijWOlhp7YhIOt99NdHVpj/G0C\n33I2mCZMLxaNcU1fDwVR8snvdQzIFJJLW4TEfwhmzIN51VAk3f3rk/LHtIlH\nOpwYMYfGkSVaVjb67Q2EOSWchPckLEA1CEfJO4CCOLFTLok1WG6E3nE0vRCA\n8HSzbC0ITUTv7xeMA4yRRP9+wvw8F2tpNO1MoL4dn1vLgm9gTZBPS6dCVA56\noO86VduNryfaGKCfjO0XKWcPIMFhrikYLzg7eHwqrQKc6kSBIVucvxfXnTUF\nWO7Vr2++kjmqchqtxwZntjhYlMutMv7jDR8CVAhV2AMqeua5ZtHmNetyc6Ge\n3M3y01ldORhA1svI5Gj4m4DPc3+LSHrjkdiiPdUe3PUtfwf9+XcjzZ9dqk2c\nNyRaa2q9L0T427taMNR0NLsbVft0JfcfQUBHkRmHb7AFHuY+TymQcVXOH2cn\nPG+cdVLO4I9H5kJShP1T63DGWgE8Xncqg+zqRsGS42IYd4SvzbqxUlivHxwt\nILDF\r\n=CyT4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBlA6mtbUiC37n7fbfFlWQWbrpQej3kAaeEZMnQlIE+JAiBsH/vsdbutirMgPqbLqO2jcVlY2hJ8Ud1tmwmEUPyKyQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.1--canary.159.c35f7fcb93895cfe42c81808341810bf25382fb2.0_1626702763696_0.8371405743951963"},"_hasShrinkwrap":false},"3.7.1":{"name":"@sberdevices/assistant-client","version":"3.7.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"54b0f352ce40dc0eed85db29c9654d30a9b8a914","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.1","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-SAKm47E2kJ0OCVWPRJpoBNLJHiDg0uhJf1XrZNc1siQ/ybpAWoz/zCPNLAdmPlpGPYZj9rK5zsDNagwwvCyRkQ==","shasum":"79f4f4b5637b723c6634c1f18690efd2d787e298","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.1.tgz","fileCount":64,"unpackedSize":1009406,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9ro+CRA9TVsSAnZWagAADWYP/014Dd/SG4Eaa7LsS1rl\nY2CwWKm2Ts+dCz5gsvk6nOM0Z9Kzo1VxxppxVAT7jVpVd9xjzkJwlsfNDgLP\nvRjMFf4VBEsfYYPCPFgvIz0+9hKCxVw4cqxyMuFBh5yxoatUuibwNpDZ3oFt\n2hYS1/6ea9Gm2ynLVFmr9dVk1S/l7CgncBAwRFRz0uNVzR2XvjVlhLwMssrO\nmsCtuXBdcYglEXeDgap3DCCoW6Ml5Iknu3FylyB7A3zYly1dTjuyJgSUUKX6\n4/qU7jDkTyfaKhWJF+6OUy2J3QNmESp2KUIY9kyyOlOnHiFZklsd/0UlwFFV\nebAjLuTRbRxKESadKGmfJ6ro5FwpKTAtfZgEmws9ljVzqwUXeqe4fV4CuNMZ\nvqw2qJPcr1lOLBLMrzgQWflgfitKfwk737UFq14yH8lQaBFlX4p/ECXiRsBF\nKBQBKRwFznCh/FEJjugxVURSTnKhIrCQ0PdnOA04J1iLGMSxcYJ0CnIXHFWB\nDbDQSnHm03a114eFRmmFmvDxLwFMb6yG5gSDR0jDMeN5hoo4ShpJdPthisFZ\nOz/gm8bObSIwd4vb80aWhtXl5VVTW2h4tWHWJ3NGcHENUz5BNfad7tN5HaDl\nacuBiz6LKAy/Azk5jhr78NeoIKchSMVwiYT6X93c84OwgXdkjJ3rpHpsIEJt\nXtnq\r\n=/8gt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE/j1m/YsrVU/JiB6Uq/thl8IiKVmKzQpMQBApuAXGESAiEA6VEGu5eFuC1NeW9jUWg5BTHY3uLLDbqgkBoWb+pRmjE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.1_1626782270190_0.10117929195622022"},"_hasShrinkwrap":false},"3.7.2--canary.159.1dcfd0d11c18b5f2035642e6e5fb8248a784c072.0":{"name":"@sberdevices/assistant-client","version":"3.7.2--canary.159.1dcfd0d11c18b5f2035642e6e5fb8248a784c072.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1dcfd0d11c18b5f2035642e6e5fb8248a784c072","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.2--canary.159.1dcfd0d11c18b5f2035642e6e5fb8248a784c072.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-TLrDR6OhFostTE/FfDryYiZkmNGcHxuOKkg/2vR9ZhZ0YiZg2LTA1WY45f1zJoKbwCWFv6JfXZEYNvB99ZjWfw==","shasum":"e29c1c2f94830e14428b3c0808a50f9551931fe4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.2--canary.159.1dcfd0d11c18b5f2035642e6e5fb8248a784c072.0.tgz","fileCount":64,"unpackedSize":1011094,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9skrCRA9TVsSAnZWagAA/lIQAIlILUVYRWfA54XHrHx5\niBmbg4BsWV3FjGyJWToHuq/lXRXLvUKykik+fC8sCtT/UELENUTfMHTpfeXO\nGH4j3xU2Nqw5YFSygF1nkmHAS+uknpyxsh4F/elm/5roQfc5TxteR7hOE/5i\nto6UGi3IPAny7ghx4HlVB38TUPNafMwvYgrNmvdVWs9/Q1AbQ4O0VaKIfbWt\nVGrUQRgK5uePTXhGNae6rtnhaafpL4X0O7rk8+U81YBxuV2rsXVWXTOrmz8r\nDWWYrVmqWmp/1whLophp19Q26rb1sn9YDC9rPwJxrqn82kQ6Eyuc8Ps7aNzp\nWIW409aGgOrJoOn2PaOh+B6iGXXgKqpeV2jHu1R37ynNmK/UHVF4TnL3FQZl\nTzFi5sDxFukrB31RO5YKpAH3ICQMkuS+c5nqSAwNCIKI0mJS1v7+Jq8i5bck\nRX5I2zcQwRWYXf9qPxz4+RVU4mkiSPiXkgv5qQafkAXprNlTPAnHNndDZEaL\nors40/TJ/du6VtxAPk0IAkFRlVx+gJKXqvx3qh40tWzWGOut9u6bayekfm1/\nlUlnywI2jCtqKAweUokfqNOPrRBzud64smzpjtqpzZvUcvDhomy/E5Ga2lIb\n4b8STj4k3o36VT1lZ95V7apJzi9tlfcDlZ0eBIEB8g1mujFlSuSkPoDWp1Ek\nVCJr\r\n=Y8uy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCBiUxPPOGC1tPAmuQIvfWyePbX6QSWyDN7JVOY4pFjBgIgBeZYf5+/qKBJ7PK3mxFEaeZ8HLmlqiENuljArQAtMKY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.2--canary.159.1dcfd0d11c18b5f2035642e6e5fb8248a784c072.0_1626786090974_0.005863033093770786"},"_hasShrinkwrap":false},"3.7.2--canary.159.51d9bd76c132db619ddf3992bfcaa390cfe8dd8e.0":{"name":"@sberdevices/assistant-client","version":"3.7.2--canary.159.51d9bd76c132db619ddf3992bfcaa390cfe8dd8e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"51d9bd76c132db619ddf3992bfcaa390cfe8dd8e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.2--canary.159.51d9bd76c132db619ddf3992bfcaa390cfe8dd8e.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-sTXEM4zj7qTF0O16iJ6eaXTswGkfclHO4F4/KRnsicEcFkiCKIjWua//cC93kcjVHAjdCy2mNF5KQHMn1fL9ZA==","shasum":"4ca1fdd4fbcbdcf41fd9e93fab49b73a67a50eac","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.2--canary.159.51d9bd76c132db619ddf3992bfcaa390cfe8dd8e.0.tgz","fileCount":64,"unpackedSize":1010896,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9smXCRA9TVsSAnZWagAAx3AP/jdgInUrF8jEzJj/WZL3\nKBG1+GvtcPPYX86I7k9V9lGsRSwSohxfZrdOv38adUuIY1QNgwea36dnTdjh\n+eyQ/5DCEP/tY3ndYK6RIwCGqUcolOyeZo+XKhEs8rNzuuvg95GQBEQ1HUpf\nQKxi8q7xowqBTs7JPwHpS9/fbhIiuRj6Ai89dwWJqTo3MsE8jisE3HN8lKvY\nnYhOkD3H6SQ6UmJfmedr8SZsWwpwJfF9EDuhTYWyftzQMIuZ9Jw4yfI1THOf\ndqdSkJwAcnOfSG3DrEqiFYOilNYbXsRkx139ITW5+HwuoY8QUzbdttQ2ARRN\n63lL3c4dPmiCTLRg+Y9aQ/J2/xRcF5RK/SnDdf4srWRzYfHOzeyZBkxk//dI\nzddVAJXz/HkQ4hD7tM97p1S1viuI/qqkvREZGHviQFTMlqgOWeMcaeTVL4Ko\nyyb0rd5BsfKHpUl9htm680PzbvijdrpfJEeUP/mmxJsW2ZaTwhMv/cO7qV4M\nn2MYWgIGsATaYijsAet48hLRTg70xNJ6Ym2unx78/cGP7wlxEiaIVSfsUXj2\nT6+gCHrFg5NCmCg4Ho690CqDgPBHALKqsI1rzYwhRDqEznkFfTlDzZC0JIhO\navJ0SSm7hg5d0uiCgjRnGwFNMfpFU7M9YbjQ3Gr1YpzjOLxv9q+Q8qdsAWZe\nRgbD\r\n=pin1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCnOoz4SGc1v6R7vGFRMyTpWoushLyZYPEtb5JKl9tGcwIhAJWTqGQgw71OVS9+PuJE8AgGCjzvNX5ArRUbsEWKQrKa"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.2--canary.159.51d9bd76c132db619ddf3992bfcaa390cfe8dd8e.0_1626786198996_0.6045975554576104"},"_hasShrinkwrap":false},"3.7.2--canary.159.0b552ec72135db8d05aab5673861c448c37b67e7.0":{"name":"@sberdevices/assistant-client","version":"3.7.2--canary.159.0b552ec72135db8d05aab5673861c448c37b67e7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0b552ec72135db8d05aab5673861c448c37b67e7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.2--canary.159.0b552ec72135db8d05aab5673861c448c37b67e7.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-+EDkRd7oVnHKlCmy1fY/tADDchliAClvEMDcu6V1v2jfMFv3KBTwuLUKKc2kS+BHlfMR0+ZX8N2KSO2NUgYEBQ==","shasum":"438d048622722e3afcf63cc2842796a98c54eda6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.2--canary.159.0b552ec72135db8d05aab5673861c448c37b67e7.0.tgz","fileCount":64,"unpackedSize":1010963,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg9ul3CRA9TVsSAnZWagAAaDkP/0SbRcI32+aud4j/2MU9\n2trFKPM4fndyKAutTnHzBx7o1pHN8JL94pTEhMBJy065+n82XpdaTo9+1sYl\nI5Ewt+9R00XWpdG0/+ksvtlIDkwDWfOtk4docLUfcZ5AKGwLpwuX0nzOI5k/\nLuwFky/cUGvO02TZn/YkJEHd3Kg0hSKtQ39clXRYWI77ptCMlHqPXteX0ujI\ngRV+R+ORzdm2u5IJyAsekrm7LOYIc2QBZhG4Eg7XDogDIrGv1kP8Okvimfrz\nuiU8ByCPAQ+1h6c1buw5O4UFfsj7KAGd4sD2L2mhwmJyPNPOrrFzQK8MgQHm\nwnfF2jz0c83Vf4NzhFQ7p+SqR7163C07v8ab+swM8dtKdeqFtjwoqMlxBbkA\njuK3oLuqBJAhT/gh1AwMqdRxA1QDmOK3CTpj0wlePp4eQHb6jF1yiglU3Jeu\n+EMloJQiHLbotxSgCKn4cwWXRRiTfWovJJ/mo98WgQnhD5ebGxL0iJdMzeVB\nOL28RIS2YCAcnRqW7EYBADxB3vZ9G73SlWdgwWsBEE3HDuVBwdsqxW/JMDVE\nd+eXtKDTFQZS+M8pOzjO8FoAFVkhaP/s8ovDdb7Vccz5MJ42yRlmB+agboUe\n2pjSaVChAyBaxfTQxluYKwc+r3xamHclMhjuDhGR9Qj9R2OPbZVasRX3NLWR\nOi8q\r\n=NWCw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCU5cLFhSnnET5l7Ecuq0w/3HQJI09ZTNHTAIY7T2/WAwIgIwABKW/Ao+AaFV/bWAgBywpq7ooFStULHPJQh5nYkbQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.2--canary.159.0b552ec72135db8d05aab5673861c448c37b67e7.0_1626794358851_0.5572366816600505"},"_hasShrinkwrap":false},"3.7.2--canary.159.36ac6f6750110a3e1b23b195067055483af17d2d.0":{"name":"@sberdevices/assistant-client","version":"3.7.2--canary.159.36ac6f6750110a3e1b23b195067055483af17d2d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"36ac6f6750110a3e1b23b195067055483af17d2d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.2--canary.159.36ac6f6750110a3e1b23b195067055483af17d2d.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-EGjIz4mCmSfufMzW3W61QpDAdRsfKVMKYODiGFdJYOGKGhgFucHzQlYDPvmEtexXP7Cw6W26RXC+l8o6BSs9Yg==","shasum":"455a2bd3b28ec96f7110854204ce60a841ef787d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.2--canary.159.36ac6f6750110a3e1b23b195067055483af17d2d.0.tgz","fileCount":64,"unpackedSize":1010963,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+CD+CRA9TVsSAnZWagAApHoP/3tqPMxPKEOGSBmi52b6\n6hYcOLqbFrf6pamTxfwqdSg/D5L/ubKl4CIsRCpAB3CxFoZle+reapJx9/2i\non1/tYzc3P7eBAwWxS8PqkHRSzcoYN39CjdQiGBe00Erdzuhrx3TOAdfg/F6\nRRLOoUM9kOIT7Tb7dcfasWZgnXf7EfscaIprk4Skx2igIy2MX3Y242SNrqao\nzBeOmHWOSGfYX2KTGUP1Do6EBw4zOliQLOX1ReTOXzkSoRukmaz+nqmDTX1T\nweElRiW1I5auEXNiTt3p5ewdsnj1gVwDO5+mLjZkkXtjFXYPBKahQ5w+rKCC\n1cYNq5J6cArxOFlPGAtkz5GmKRtzbPqMp1LDd9XXqksPJkuRrBcZABXUsc8i\nSTfLtxoKPvKSoaJn/1JsqCzF2aFceN4THU24o6pRDTmzHDxkLsiYaXE/ZcVj\nSBk4HEp0rUXuSxSiyge1OUrH7roWBfTlWsD/DfGsCYYsMdLumKul91I/aRRj\n4gvgtssARfR6qtuya9PSAyUwANnOPtFdUFATEDYI5lZJq+sy0sXRsD0mKu8J\n0mtEQ4rrnsDUpFkHHFcaP2KSSTaCGILF7FGRqNgOtp2Bs/dcYRRGIwTfZWHa\nFXE0FGtSP8SHoyLF1V2oQ7AKPWIm6TBLl3JV/YJbmBQ7CoRyqZ6jdpY9T83L\nkGlY\r\n=e8zb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHo12sKfyI058zZ5RdDWxbhEgm6ISMFChMuCktQC5r3KAiBkaL2n8Vtx9aQMLQrJ3hBagZnzc5pUoRP4+WXA9hq7nw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.2--canary.159.36ac6f6750110a3e1b23b195067055483af17d2d.0_1626874109850_0.622654647111107"},"_hasShrinkwrap":false},"3.7.2--canary.159.c437c9a6861497cc1adb77c1ff7a071614f7d974.0":{"name":"@sberdevices/assistant-client","version":"3.7.2--canary.159.c437c9a6861497cc1adb77c1ff7a071614f7d974.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c437c9a6861497cc1adb77c1ff7a071614f7d974","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.2--canary.159.c437c9a6861497cc1adb77c1ff7a071614f7d974.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-LRTr0zIdGTImvDnomfu5Hyak043ORduDCnBEbzg1IrHYX7VIYhT1yzDeD9CDc3OE/GmAigFvxf97/MILodv5Kg==","shasum":"6add0b741412cbece72e04e9c4ef5e60eb258b2e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.2--canary.159.c437c9a6861497cc1adb77c1ff7a071614f7d974.0.tgz","fileCount":64,"unpackedSize":1010963,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+CTbCRA9TVsSAnZWagAA44sP/1zxeIrYCwg2qUlJD+ma\nIlrX7gXMn/pz9DRQ3tC8InzVX6rV5AY5LfDnu1ONVMBloHVcUfkh90yA/+wB\nlG1QR/ZhkN2ZyjS8ZXNAnewnXMU3brZfH0fxd+LLlY2QU64VeiLOuiCBFtLG\nVwUQorYY0ln4Thn8Ai+sRkFMoVINvBMXTIpvCMLKRAHWu/sXYRB+zZJY2Ydc\nYMkk3qYJ40TbiIiokDO/6KhWQVEQonHrUMcwkddFPIk3cod9TO6oMOgMXVKA\npQgab1RngUIWykBz2qZd8Dwt73IZq6dK5/ThKggwXCTt7yKi8N+GqmBhKL0Y\nBTyBg0xgEYPP+LTftYy0TgmHUd5+OO2DKXZs1thpRALQW/8dEguMsEWlHgAn\nPTQnrOGSOfGr9CGz1zE2UnvyzejuAXceMDzRE++1Oi7bxDvDf93Z9IZAKka4\n+d7cQ3iNqF3e79H55zExkjgWOByrTa+0d+fn/eAMcIroSKK1qpvJAzm6jPJd\nHEY7sYHgzEs9rZ5wNlDzmLEQ4kmJvS46ZjDzeMn8DT2dUneRBkrqacr5kE57\noxIHljeRS1aJGQlVlqvDI75nWbd+4qh/m//U+hCYcLAeZi1306hrC7SDhFsJ\nvrla0bdyBndFJ95O1lkuLRzbkjrkNtZob2VkH2yL0etzbhQ9amG34JxA/R8/\n+9SD\r\n=SQBh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDCEho662kEi0++NkcQs7w4fBf/Jr+94zYY4llr8/F32AIgMysI2ElcpiPYhY+OxuxghkGrwkzNvGzrevP1mYjIle0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.2--canary.159.c437c9a6861497cc1adb77c1ff7a071614f7d974.0_1626875099147_0.8440785437143725"},"_hasShrinkwrap":false},"3.7.2--canary.159.37ed9c068bda10b90e00f745b55b62b8fd0e46a9.0":{"name":"@sberdevices/assistant-client","version":"3.7.2--canary.159.37ed9c068bda10b90e00f745b55b62b8fd0e46a9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"37ed9c068bda10b90e00f745b55b62b8fd0e46a9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.2--canary.159.37ed9c068bda10b90e00f745b55b62b8fd0e46a9.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-c7fAbDmQVe43Ltoz2H2hPOWvjBtLWDj2Fldtf8PsXZ9pE6C9FdIX5Cgc0NAbHkKrEae8n3N8bC5Wuwf25zQ/wA==","shasum":"43da4e95b0f6f52b4582c92c092492750498932f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.2--canary.159.37ed9c068bda10b90e00f745b55b62b8fd0e46a9.0.tgz","fileCount":64,"unpackedSize":1010963,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+CeiCRA9TVsSAnZWagAAid4P/RIsbwOF6oTZwsbQNsl+\n18ZlnK9rTE6uPM3vtFfjom24Q48yAJJ9z6L4qgJr6GFRZz9769YtBTTseEQ/\nC+kN7nhgk84QxDq74nmebK+42n4y8twAZV19NWoiTlyKU4dzMlWyhR8hepfd\n6XsfFCUvz3qqi05rPiHO8vzNchpnD0sm7ruhs41uAIuDktkaM6shI/Ft8PZA\n/g7TUIP1MHsITpGEDGre1BXSWbaLFXFVs9A8V/ZTAEkpjLabm5tHOQtCp2+U\nV/6q/bJxNl+e8llDLcs+enLb7+eNQlCdsZj7YDmZuf9DZTfA7Fk7yICqon1R\nrVWlrSnPyIyILZ0vWN+3KObMy2ryhHJtm01h58VKDnOf2nkDL/xBh8DzvEjP\nHX1NeKD78oN77nVg8uWBOIVUVOcPJdiBh36tIBOhTrc+1RGQZlV6FWHXmjCq\nRVi4dA8d7T07sK8FfiWBS0pq6Zdcnuyada2MTukn5tAjfmeI3mzPCZbg1caY\nENkTCW56zy0yTTCrlnmVPLr91Gf9ujdD0Wpo1L/1TpxOKANkEB1aIXc4BnOu\ntKtgC6tNnbPewhfE/lAIinsqATfOO+kYqKwFqPbUpwNtTPlLHIueGyYQxxmI\n2u7Q8Slujc/njhsWdaNhK2tG3Sd7I/a3wOw5bGZ4gcH5+qwLqnpRargXtPa1\nuGUK\r\n=ymYT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC0dBZdMoht5wkXN3+M/6kDryxAOiFpRXgrA6seMRJy8AiEAuvWhJwU4ckUW1IG7NLnYleq4Yjt3jSqaYfcs5J3whE0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.2--canary.159.37ed9c068bda10b90e00f745b55b62b8fd0e46a9.0_1626875809944_0.042521542736664086"},"_hasShrinkwrap":false},"3.7.2--canary.159.0f3bf007a57e3be543a5e3862a170c434367ea6f.0":{"name":"@sberdevices/assistant-client","version":"3.7.2--canary.159.0f3bf007a57e3be543a5e3862a170c434367ea6f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0f3bf007a57e3be543a5e3862a170c434367ea6f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.2--canary.159.0f3bf007a57e3be543a5e3862a170c434367ea6f.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-U6NMm9SPHLgwFxgqglugWscRC+GUW4x3JgxYlfd4FKXCpUi8lCRon/dBvLvmAAA1vZYcUqFoB3i+bo3f+HEbCA==","shasum":"3e86490c9cdd363583d975c0bedc70ce8f9be0f3","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.2--canary.159.0f3bf007a57e3be543a5e3862a170c434367ea6f.0.tgz","fileCount":64,"unpackedSize":1009638,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+R1ZCRA9TVsSAnZWagAAwZIQAIPB5cvEPhkPsKQal1ZZ\nJxLSoN3aQlPgGcppTx6qtDkyfbRwu1Xoo9c8WOFebFg4M6MqpIzikrTVJCQY\nxdS7GA76BOIA+1AVCtIKYM7aNqjCMEpqYmKPo8NKQ7ZfjbEQPjTx598wJSzc\nTz441D5RKhkT3I+NSTm8jqDfKTmj7jMaygQ/Ugk0UDX3YrSYQjKZyMrf1a8I\ndq89r7riZbtpXwIbsuKU0iffp/rR2Po8c8BNl1/Ob6pGs2weqxcAHv9JILZg\nJngGtsZee2vuZZEJpKJwtUFC61jyURjheIM+2zDk/xAWVrt/gVwPmKHvz6z7\n5WwE+MwVD245Y/wuhA4bde30+FqtIYWa/et1Q5z9lNZ0a8s8grrVoLiLhcNi\nKPErHtZez5L18jd/3m2jxDuvcRWcoX/ENp1c1W4fVpTobBd05c63BdvDtm4Y\n2PJZ2W1IkLXjKu+uEeuZgS8ClIlmFBoW0xJaTUDHIB+rsr5A1JwmkKfcf57F\n0g71eGB1k216BNjoSrHlk5p4ENnr7+80OjC7+loKkecTgjYZs2ilRHz/LshF\n96uPkK4Vtvo+PCUK+v+buDX3qXOfTbTp6fkkC5A5DZKVtpPvMyYR6X3AYti9\nHAD4REoSsI8kTu6LARDZyU1lSWTqBFiX8Upljk/MUsnxGAc6AYq7/XgivZJl\nr1sj\r\n=Md1T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCePSu7APv+9wG30wZ8rhwYaKMi9gEUvCtcM9oOv5RRJwIhAKwSlG82mlAFhniGGv8cBU2RD4217A8O1MN0cNxV4UPu"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.2--canary.159.0f3bf007a57e3be543a5e3862a170c434367ea6f.0_1626938713197_0.22064864149942887"},"_hasShrinkwrap":false},"3.7.2--canary.159.3a997572dd009b435aec9f63b7fa85330761d6e4.0":{"name":"@sberdevices/assistant-client","version":"3.7.2--canary.159.3a997572dd009b435aec9f63b7fa85330761d6e4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3a997572dd009b435aec9f63b7fa85330761d6e4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.2--canary.159.3a997572dd009b435aec9f63b7fa85330761d6e4.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-j1cQQveSLqVKAVAoV/39JL5IzDRHz8xA0kIPV5Ws+9inpcGsXrVBUt+ogMswg2iU09IOf+vOgcxQeNExyuBktA==","shasum":"b87f75f7ec2312c82935faf513083b830c054617","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.2--canary.159.3a997572dd009b435aec9f63b7fa85330761d6e4.0.tgz","fileCount":64,"unpackedSize":1009638,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+R26CRA9TVsSAnZWagAA47gP/3DGoGeemBifGSo4S/g6\nl/Vt3d+0cZFJkF6QVzYFBpyp2MmIcguNdxnjzQvlUOElVgc5QUe2858aGRp+\nEyVtg+bOTH2u1PfPRoUrSNfAqK80R/mbfs3u4a75UFYUfnGbS8eYqNsNpRY9\nFArZY3CT9IAMMYdeLVUCJGwoOI2+RXHR9a+5k1UJ42UjmnhAadt/fRaXFmqk\n9CN9YhNOSmMto7LQ1AAiuSBCor6SQtWzIvg+rA+HN0IZ0OWt12BRjfKP7NYo\nU56e2v+842IomVLJCn0yOTPUg70D81GLhncJFr2PbQshmZt+wx8VxVfFpbvQ\ndTaAbgV/C5RCaqPxaFhlpFcXInpp6O+2BEn7HjtCeZYjcEXmAgkfbPDJhRpH\nxm36utZl5LUCxGBTpXBKjG2eEEhcrTVSJlxVNFkTqhSd7F3VLQJseZSGDx8Q\nz60H21qooG+HDTKtTo+dxQ/fKJxTa6IFqg8URd4DbEIyDjX9ODYASRFSrgsH\nKassNDlnUJR+eHNKzaz/u7jpRsusRtlMNOQea1sZu9zNNtN2NjEo2ge1AnT+\nXMwpytiJzFjrCHSIWyE1HFALT9Uc6O+ay6dSfZDZw8qUMRaU6mW3Yo14dwkD\n+RaBRMsutKzrSZjAaCntQ3kKT8o2HN80h6pPSrVifQMC1r/bOiybDhYM37yM\n4mz8\r\n=3Xvd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCZaSu41l6EZD/z4kFlF/849gOthsY2Y9TaxertZ4JXMAIhAJU1ExNe5Y6x1nVmzc0G3dWPkWGEz1wSSjZPs8vlgsc7"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.2--canary.159.3a997572dd009b435aec9f63b7fa85330761d6e4.0_1626938810718_0.951958102389808"},"_hasShrinkwrap":false},"3.7.2":{"name":"@sberdevices/assistant-client","version":"3.7.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"14032853584ae398a9eae1f896459abe741a19be","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.2","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-KJUhnfuG2ej7Zn9JkwxMvTcr/vs/8a/BS/QPZHaEFe0ahq+N7AlyB3w6W098d7aYonIoWoy33Js567XdLUnFSA==","shasum":"3e118d495ce6c439709036820e76cbd23bb11f5a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.2.tgz","fileCount":64,"unpackedSize":1010123,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+Tq2CRA9TVsSAnZWagAAXdgP/jVHE1IaQ9JpVu8P/9my\ngLcSrB9DKRda2Sv+oSxDMFCN7pyPkEGhtSMw6vgfbI9xmlh9dStgvEd/wboq\n2B3CTiV/Z8RjmyqzKnLHz6UQoL7ozoRLu2xWskCuhXWd2/ycKMrVxvG/XO+c\ndHQFGPBwHRQyf0j+NrHcRO1fW+tP99sb+DtxT9H/WQG3FBHk7ifSjtYtGzYX\nCZxOXuGTbMduy7UwgfSco2CklmVUCW2YTszZU9icilbsqUNY4ZdYvbg7Ih02\npu66O3TOtdajuC6A70UsrCEVqK5cP3+ktxNPDkZGZvLyyHi0iToxv2JN3HPh\nJoszBARv4Kpq1H0D7thjzf/Ibm6S7SHGFvGK8JV3qdJIjRlumcKe5zraJW1h\n9ivRve2YRJXvcty1Dps3lTN9SYITClYu0J60EVystvzmmpWB67xjLzM5z+Y0\nT7wN1XcRozahPR4o16REizboKNqQzw27+f5aPYRS68xwWZxB00tqu9s7eLZs\nYnvor38q2r6e+T7meefiDErJlQPYsNagZsSeaC+OlyoboU1e7+2C/1lEBQ05\n6WHQ4ru7wl2xgJ3OMFCVWqaxPrW7FMpFJ7gc3HF68dseAflMFg5UxXtc0Kxt\njihgpG9MJg6ftm19DPaMBN150p3SYwO4zATAlsPdMLZ8PoFk7nQApEKjFoZm\niPkS\r\n=uT50\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCU7i7QaUdkfTi9Gy/THLrJblzBtYYptwiDPV6cTsj6NgIgPYHl0nnXg3g9DCkDtx0XaVpDYtIGMuomfpK2nb5HOkw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.2_1626946230727_0.6185286588620125"},"_hasShrinkwrap":false},"3.7.3--canary.160.bab31c8ff3fa1115816c96991662909f8ee551a5.0":{"name":"@sberdevices/assistant-client","version":"3.7.3--canary.160.bab31c8ff3fa1115816c96991662909f8ee551a5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"bab31c8ff3fa1115816c96991662909f8ee551a5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.3--canary.160.bab31c8ff3fa1115816c96991662909f8ee551a5.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-JqQ48+t1PV3GJALrtixG+5nEXS1dSqWuWvl0fZ/d7LTxs/XKHbcssj1ESRjB1eiGJCqHPzmdmd8NCjlb3jLHdg==","shasum":"de73a4d7e09a4c87b8fd326c6ec1a3ba3dc27cf3","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.3--canary.160.bab31c8ff3fa1115816c96991662909f8ee551a5.0.tgz","fileCount":64,"unpackedSize":1010376,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+VwkCRA9TVsSAnZWagAA3E0QAJvY6ODFsYJ3RHmfYZUJ\nB//OljnVcfONC/rTmb2c/DXMl/nmCBTZeNOnaR/+APlb+hg9Y7kXb9KLYNoB\neuvKpXTy3wEmFUo+RBvRIfs283QSzYP0rXl5FxadYzMsknUmlaP+gqYvuRV+\nMcFA4hgValDaTsXUus113mKyIPfDl/9fJB9gVevjJl/joPouGkyOVn6EEK0H\nQM+m17rya8/In6koT9PTqu/gRtTnza/shyNIUUry7EljEWHzEIXxXwHe86ut\nPHTFMVHbyef98zWnL1Uguyv2usl6kPeV1oC8Ic8FuTsyiXN4srTUeTjJ3urZ\npjV+r853cU810rkcfif7ke7xW7wCk5E4oMRMY1WsBr4cHfe2hF5CiIUV5nSq\nttvrdLfnTVSIs/ImAI1DHep0LyxTjplQEUi+0j726z2CKnEwrMRksrgKqS//\nr6Tli6U1H3uUIltKuXwCrfx6yYPMFfFYGDna0FKa04uCYHIOfJgOifxPTRlp\n625jeDu3B7YJ51VWYkMiLFI2m2mmnYrV6uiLZerf9znpmNFzffaLUTX2L4wz\neK7HBW0bQve3No0gruF3hKYpeCSdstE8MmnT/mYgi0PmLh+O10FAWEIQ0z5C\niNwYnMjwhOZgrtf3M2Jctse4I3Rd37hwE08ALH0ZKTUiQvN4sNJRyYLQ2tGl\ninnG\r\n=L5Vp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDaKJl8terPae9452u3mHqTdIp6jWWzOhaQb8nACrGdHAiEAnsnvvE3ffHmSTA5aNBwmk+ZLUMtqkPWrRgz4TZ082xk="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.3--canary.160.bab31c8ff3fa1115816c96991662909f8ee551a5.0_1626954788203_0.8568965579947381"},"_hasShrinkwrap":false},"3.7.3--canary.160.03a1d2d4deedd9a6c67886f55a7dbb408995aea7.0":{"name":"@sberdevices/assistant-client","version":"3.7.3--canary.160.03a1d2d4deedd9a6c67886f55a7dbb408995aea7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"03a1d2d4deedd9a6c67886f55a7dbb408995aea7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.3--canary.160.03a1d2d4deedd9a6c67886f55a7dbb408995aea7.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-c/erM0f/cFpu7TbYqrj7nSFOX6AJgy+3r2GNLol9f5fhITY8XxBcrJAyLGpGVA8XkjU9pAinzT1/q3d6CmZoZw==","shasum":"1b6e2830016d4be73a04422f72f748352fe1ca8a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.3--canary.160.03a1d2d4deedd9a6c67886f55a7dbb408995aea7.0.tgz","fileCount":64,"unpackedSize":1010287,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+WOICRA9TVsSAnZWagAAYqMQAJ9NEOilhDk3fnqlaf+5\nLemFRB5g3qp51cdi+pHOpMRiv2s0RAsx6hN78gAOYB4qvYZKtyV9+dGBTzfz\nl22frJ+i/m/XWChWm5+1DhOZcZH0Qs8ZW+suFcsm6M9dirkYKmN1M+R91a6n\n2nk1Zdpo89u7Qz5f9AGRgCeKwMk5flG+O9bVD+z+usr3sHb2O39Q5RLb/zw0\nnQIjYKJ9HPtDtLAi2PsbPfTmlZBiIZAdgGTiJKgDZVv0QabrOKCMVS3KLTBf\nMfs6ySzF0YMpCBEAPs+YXqK4ljq7ML053iqLeIAuRgpm4LF4tQlgQYoecrqt\n9GcGtxM7gVSOTubLXLYg1DLXsEi6TxuM9J94eA9Ni0KvSM3wALgxD1glRvZb\nVf+mEypKvAg9ejmNVHTXS6dA/zrSQp64KvNNpTB15u3Jx4BeT/Ca5gWfnOZd\nTYXVVIccXxzwDmbu/FIjZBYWfhyJ3Nh67hcdXR9GPWrkIpaHxlK3iHBztF/z\nxCLum7fwj0+q3z5IovlEI0kUge8PDMyNEoChBGP931DS66Klkspjeve7ygQg\nEuSzfMtQMR16dRWVh8Joqvs6Fg6OCS61ibk39UzdjhdEMvpoJbkPK6vo8iyc\n861dJ9QHAbSOA7A2AAsi/WXgeJpo3xT3BGxL1U3CuITAVR4NCs2tD5mH0W+h\nwcMS\r\n=0ADp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCP87SqaL03sM46ADluyDvVz7eONyuVHt7BOpZNrP1VWwIgZv/hgu6W2cxe84YUmQgfebrqsFQnNRoc2gKY8mm+x2w="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.3--canary.160.03a1d2d4deedd9a6c67886f55a7dbb408995aea7.0_1626956680253_0.2859148071022275"},"_hasShrinkwrap":false},"3.7.3":{"name":"@sberdevices/assistant-client","version":"3.7.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d88e5db55a0ed69404c040ec6871e476f8afac1c","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.3","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-h2vQMBjmFQX5XXj0/4G2KRSo9B36Pstg5ciPJXphiCOtCabFxDCMQHOgAmXleLb3vd4J9YNFoSVurnOVvpoYYg==","shasum":"a9745a178eef63fb4f53286239098e385d99bcb7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.3.tgz","fileCount":64,"unpackedSize":1010425,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg+WgrCRA9TVsSAnZWagAA564P/A/mtxq8Bv+eYd4Qk7QK\nplraUXxtjgE2+Cb833g8SCYgdSVxsH7zIXxB9Vz57hg6iGnK/x2Dw4LeANdk\n6SLO7j5f1MeC5NauMTbZVw5uFhW1TMm7N7D1dnHK4mXkGVM1eyPa+WqtgEqR\neCXycKjLuQHo5RXouHmLsfHgr1Ki9cJ+mbp+cEW5gkwQjuvP2ko5J1Oi611G\nf6c+TOgOZBs/MysRcsKlgUZbdza4tYOVXrvek8Eui9x2DkSl0z8WoIReGBmh\nGKytr3me9eE2ZV4Gi/0t5TWGYPPjfzkXfv1rCaI7SyhuobeKwgXOsseEYB/q\n18j78zLZ+WI1kXNXsr/Kl4lJrmJYE4MVNYvfxbjIkdTFj7LVQf41W7/BdXnr\nPEXfkwQqU9jPK9ssGTb6aSZM2rKJLGsLK30mR98byxQsXh+WujktbI8HhIDB\nl5nhTPuPZswNtkcLQ5x9CPeXMbOQTuHLSzsdkA+raFh5bw4TJQg5r4M3mR4p\nIgTvUCuRm8kzlRmhx8pIr0C4YSJOGP4bFZnFKhmzH12sa/xZeIpSIS66HauP\nmasquyEQILOEdwZuRTeu44bjkHvR9isoO5XCopdV0pQUn8AN4nHaTbQ9FP0K\n/wvGWXlgq0h0yCictn3vxlbN00kN0rGd3XrHRimHuePdgqdEOev8YiKft2VL\nLcrN\r\n=dDRb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCRWrWoNOHphSL4zUqmG8OEfuGEtcASKtTI1vbzg1MPJQIgNO9xGs8TmoYnYDMJPKJu6Q/ceKY/8bnhK3TDd00H2lA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.3_1626957867027_0.4315133109326206"},"_hasShrinkwrap":false},"3.7.4--canary.161.a406425a0f940689503b1ba8da62c1af6f5241ad.0":{"name":"@sberdevices/assistant-client","version":"3.7.4--canary.161.a406425a0f940689503b1ba8da62c1af6f5241ad.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a406425a0f940689503b1ba8da62c1af6f5241ad","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name', parameters: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { action_id: 'some_action_name', parameters: { param: 'some' } },\n        (data: { action_id: string; parameters: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ action_id: string; parameters?: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendServerAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  action_id: 'target_action',\n  parameters: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendServerAction<SomeBackendMessage>({ action_id: 'some_action_name', parameters: { someParam: 'some_value' } },\n  ({ parameters }) => {\n    // обработка parameters.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action_id === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    action_id: string;\n    // Любые данные, которые нужны смартапу\n    parameters: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              action_id: 'init',\n              parameters: { notes: [...ITEMS] },\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.4--canary.161.a406425a0f940689503b1ba8da62c1af6f5241ad.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-q8gjfKPRX98MzX2Gyh/vf1upU7/hXQnZ0p3D8r40nLZ5oFQCwemuJ1A08pmm3clda2qjF945X+ptlI93uXQuUw==","shasum":"03244f51dfe1cb7a19d5964175749caa462b86b6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.4--canary.161.a406425a0f940689503b1ba8da62c1af6f5241ad.0.tgz","fileCount":66,"unpackedSize":1010964,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg/nYUCRA9TVsSAnZWagAA7+kP/0diFFNlwXiLYOnWREYM\n7CPc+AslrQ4dRH8+i9SUruAZCCcK/0G9gFosUKMcA94+JzCneKhxx4RlCO8a\nr8BfGxXUAxb78X1wrlUNgLOGg/FvievJK5In7e0IC2ikD8fu8G6rfOqr8q9A\nCey3625J0LSPMOcPuJX3S1K/7pSDj8Pg2sVNqRkJ1nzdJ0ztFFNRYbl4RU4R\n86VLpTTl9F5RKmBzXRdKm82aM/grvxcF5ASnZoyl0JJroV9eWXNKfimx/k2+\n7LBfBDdk/8UcLrl7bXUPfabaJcylITcG52DjTrCbXbonIplyTo28OvG+aAJw\nibfbGzhYgr9JAkwAj03OMG3KdHMVvDupNlS5jT22+nSBKMiQFeVIbMTQJOs6\nhMMSWwuAy/cc8y7psbHOkdbzo7sm+vA5IcHM86Gj7MeIOXxfAzGT4FVvOpLM\nzUqfHVRmyIbLN9U63feUiTrX8XOhjL6g//GJAgtGFPu+m3G0z1aDNLQOh5rs\ngv865BYUe14KIiOhlMu23p663ZhfcX1Tv+4odGkoThpi0Cz73Z4tIoS4Ie5U\nKf7iczTQWD1KsVFhjg0H1iOTT4a2QMeyoybSgCMnKecAw17+bpKL/rT+vFb5\nN3QMUu4+5hMNuWncKsZRj6dRv+2AMR+7hAH40qUwKTe+WcLhT3TAVAPdjnEX\n4jlC\r\n=b63d\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCwBVjRASerKIQkzMbDVDGVw5owPHRiGE4Wi5QS5PDNdgIhAJOqfpzlitaHnFA3c5rlCyW5T8JL7rZE+4BtC3hqtSS1"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.4--canary.161.a406425a0f940689503b1ba8da62c1af6f5241ad.0_1627289108087_0.3529084814365697"},"_hasShrinkwrap":false},"3.7.4--canary.161.fefed6f18241c5a30d17cb07784677406d52276f.0":{"name":"@sberdevices/assistant-client","version":"3.7.4--canary.161.fefed6f18241c5a30d17cb07784677406d52276f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"fefed6f18241c5a30d17cb07784677406d52276f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { action_id: 'some_action_name', parameters: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { action_id: 'some_action_name', parameters: { param: 'some' } },\n        (data: { action_id: string; parameters: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ action_id: string; parameters?: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  action_id: 'target_action',\n  parameters: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ action_id: 'some_action_name', parameters: { someParam: 'some_value' } },\n  ({ parameters }) => {\n    // обработка parameters.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { action_id: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.action_id === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  action_id: string;\n  // Любые параметры\n  parameters?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    action_id: string;\n    // Любые данные, которые нужны смартапу\n    parameters: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              action_id: 'init',\n              parameters: { notes: [...ITEMS] },\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { action_id: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.action_id).to.equal('done'); // ожидаем экшен data_note\n            expect(action.parameters?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.4--canary.161.fefed6f18241c5a30d17cb07784677406d52276f.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-/2s///dggECahulmwmK9eK3meGycMG8hNKO8VSeHOv2eC8wdY0EcBC/n4MuR6cftwleVqusp4r0DwkK2CSj+ww==","shasum":"9016f5303228c6cbe47c9a20fc710b2ca6e8fcde","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.4--canary.161.fefed6f18241c5a30d17cb07784677406d52276f.0.tgz","fileCount":66,"unpackedSize":1010952,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg/nZWCRA9TVsSAnZWagAAPQgP/Rsuj0UeSAVZLms3XlKx\nakTIE8WFN+9vd+jzLpbDG6FpJo5JwJlOgzroy2v5+S5xRJcNBHPlB7ocGIFF\ntdlCmRdLxRy6AMvmzdT0T4aKH961DvyJW67t39QNyAzB/q6ZVWjPPtSXyJJR\nmuFyLZdkuUaO6AHDTQyvco2J7Yl1C2XPrMCEnt1GoWXwN5lRyz+3oAHtEe6C\n5r+A+0fo+66pfZALWDaQX4pHZ5/QjMc3Yu/MrJnq/IZY4mUwzEG04crerCWM\nH9MAUFApggKapsfk1ouEiG5zwvS0YFToCqssn9AJ00OmwgymyO7Ucve+nQVr\njuEvj+UH2Xk8wXqlX9CnrsQq6/r4Q8wrwWeR2dKKXGS6KNw/K8Rqz1baPIpZ\n8PIYtvhSx/Je/AinNm7XYR/ZpymzJLf+b9ld5EQOsjTdfmLzRKIqFKXmvudI\nV0A8WFomnmW9kS3OAWMeDi8QdUxyrb7vNo1AqtTD816INeit+I0LxM495/Qa\n0CPuXzvTf4MT9OJpVjKHH2asKT1+AxDbBlJmmw7/qQiaplmBY0PGD0HFdyZl\nkbcGMxlfbIYqdpFOcWmlp4D7cXf0azzV3iN1neQwlX2en9jVxzxCey1aGdBr\nu7rNsZ+57AprqmB9G1fdbe4hPx7RciQ02NcL7q/ecMPYyLvtrwzXhycmsvGe\nSAqa\r\n=ngKF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCQAwqlVQua7zpyDWsSWIbovTdhifpzTp0xw2Zy2khrvgIgay+qENkmwSUV01LQiWK4sxSXiGUoa2ih+VQj6iq6owY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.4--canary.161.fefed6f18241c5a30d17cb07784677406d52276f.0_1627289173837_0.9654889485436859"},"_hasShrinkwrap":false},"3.7.4--canary.163.6b1e802453e56533a4da3abe6cd05828f53b22c6.0":{"name":"@sberdevices/assistant-client","version":"3.7.4--canary.163.6b1e802453e56533a4da3abe6cd05828f53b22c6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6b1e802453e56533a4da3abe6cd05828f53b22c6","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.4--canary.163.6b1e802453e56533a4da3abe6cd05828f53b22c6.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-8PLKTY0UNnsN5iL3qrvWoUzM9+A60AsBrGBfT+HW2t3PlbHjtS8eoIhexvy5PdZKOwA0BVatbfjmzJuVg7H0Ag==","shasum":"e3a3e6bd24aaacf76bd7c8efea8d947e04f70a6d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.4--canary.163.6b1e802453e56533a4da3abe6cd05828f53b22c6.0.tgz","fileCount":68,"unpackedSize":1011524,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg//92CRA9TVsSAnZWagAA+b4P/RlZzQNY0koLZFOnQXUU\nI/5rpCpXslhP6D/sZHp5nmzaZH9YnNXsgyS2N605hlzrFFnE8sILA79ROw8L\nUayEihem+sOew/Hxd4I8qqjWYM5NdPrjU76YwC8AqGuBRtik6SOo4xkjCS5A\nV2vgumAN3I5RqbB+EIHv1xQ3iKFC0bU4wTaECzLGi2L1vgV8NJwVvqKGRewd\nXVRlhV+rjS3ki562jFQj5bW1n3HF8RKM9mC4MH/f+R3V78vhEjg5V9W7P7dG\nqpginz8kunw4tVshuSf7MHi6Ug2kspI0cAKAISVnwO7qiYIealBg7iPvo2t7\nb7rd+tTWMMxECf1WjD2lDPWy7/miS4vUIecUxkmiaFoGJfPuTnGtxj3J/EMg\nsEQTk2WDIMFPAtMk94RhV7oHNZrw68ETaCV6w6mxY0JlUIh0JqprOu7cohJM\nBCNkgUidomZ9T45t9akMaQllXkLRGHfu4kgV0VGq+G4X+BLXYw2+wVjpwWUG\nFYGyb+y1hOzFMN0BclqA6QLNk72GLeP2ZlZ5srQbrq49vQ1Mdmfr+zBv7Neq\nl7dQEbtk7Agt9JPxegXIEjdpn0UVCDTqRn2EOvCHMh4V8NOjILyie1YJRnpM\nipYqUC1aIr1MUhCLuQ54vwg2ZINJTRy52PNdLjsB8ScAIzNkjf5J8tFLVDcw\ngRsR\r\n=n9g3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCOTc2muyoSMrTQ2d2iLc/LoT6YBxPR5kgbRKtIfnw3QgIhALpFnEL0X/e3cXDcDN1m1TjbA4l7KWk0voJa66lGcuYf"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.4--canary.163.6b1e802453e56533a4da3abe6cd05828f53b22c6.0_1627389814023_0.988296127503631"},"_hasShrinkwrap":false},"3.7.4--canary.163.d6c54bf84d87726fd220abb50e382ef3777ae707.0":{"name":"@sberdevices/assistant-client","version":"3.7.4--canary.163.d6c54bf84d87726fd220abb50e382ef3777ae707.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d6c54bf84d87726fd220abb50e382ef3777ae707","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.4--canary.163.d6c54bf84d87726fd220abb50e382ef3777ae707.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-aFbbWJ/DcdJ54pLIguRweZrot/Aa5t/RHo39l//y/yZ6C4Oo4VANLOuzRH42ZFArxo4K/pLvs+VGy7YfJF+czg==","shasum":"28a9288f46fcc1b50573f766ed2bf36f8475db30","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.4--canary.163.d6c54bf84d87726fd220abb50e382ef3777ae707.0.tgz","fileCount":68,"unpackedSize":1011530,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhAAhyCRA9TVsSAnZWagAAxpAP/1TVzx43PVHefpEM5MXS\nsbG18z+PXbWcPP3IZkFF5OwJ+hMXiYqBM1h28GhbsSEakevZaq3+aqoax1Ua\nkD44waFKUtfomI9wjd9p9l0nETaINLlKNIAvEGxEeTSAkzbaTmG11Gvyna4w\n6oEAawLIci2aEMqvByhGkmC0XNu+7sEWbBDxuMfPbHmG0xC6Wf8EZf0f5Pgn\nhI/6CQbw8e7MSAgRyX6ZzA4zBpXYl4zRZk3/ejYwRDl7ylfTELAJBjUM5Chc\n5idCwjX/fUndB9q9rj2JVKczezbGnrvRjQgang7pilkAh3nt3F6t7AfNira4\nJ3vBF/Kc1LHGRwSjoddKqydTV9WE59u86lNI/onvud4bR6FCy9REm6+qQf/f\nWGCMD51lYvX42h02MBbHMe/Xw33QfcWuWkvHLslpNnMTXR2hXoSYVV4OX4FP\ntr6oTPqRFfrKDIcqMooWPwOLAiQkYEOqcGw674jPbgCAdBoxiUAjAan70a0K\nEwiFXKcgwt9Xrow2yY6kG08nPPQKFIsjnivBV1PIuFoquGNZBG3zJjEs+6L4\n4n3/9ejP/kjAqFbxed09KPKOulU5wp3afiShRYham005+YfN//hqLneHeB+9\n2ecKiQhxobRCkPJiXzvVFx25m8V1ha9xQ0epJN9et97KTGUqDRuoXPVlzxmk\nB+mg\r\n=pvbU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDTy4cBeg5jN7r/UGBpstHz1y+GYDtD7KFOUzTKLzbUwAiALSUxeQFFLmjUCqJQegA45Pwqa9D3n1m4iIEKONlo5+g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.4--canary.163.d6c54bf84d87726fd220abb50e382ef3777ae707.0_1627392114385_0.7744398785396085"},"_hasShrinkwrap":false},"3.7.4--canary.163.97727bbf6a427f7ca1d6e1f2bbe313a261e592e7.0":{"name":"@sberdevices/assistant-client","version":"3.7.4--canary.163.97727bbf6a427f7ca1d6e1f2bbe313a261e592e7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"97727bbf6a427f7ca1d6e1f2bbe313a261e592e7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.4--canary.163.97727bbf6a427f7ca1d6e1f2bbe313a261e592e7.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-IkuO/cjSQAuNM7ouCVcuXA7oORFwBBkVu0nqOXbvLYNSUPjaYkCS5R/0R1dNMsuEJNB8BdBJ3HResv8gK+7law==","shasum":"5635127897ae1aa6688e457bfe9d3030fd350554","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.4--canary.163.97727bbf6a427f7ca1d6e1f2bbe313a261e592e7.0.tgz","fileCount":72,"unpackedSize":1605696,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhAAn8CRA9TVsSAnZWagAAqIkP/15Cj4n3tjYIJSIJTN+S\nyRiLMzswWMNn6D/fFKqoaaJFUjXpgLdPEZEUVO0+AXxn3NXUHABTA9ykUXPl\n+htGeZixUqa4etAZWKKmlnfU9lORT6DsvGlBY0Jhn7ajHyVA7Ik1TplV+7aQ\nWJqoqpA+PBKwF/DcifO8IHiYcuj5t/CIJdWUZZMaVwtMaV1/ii7gJtPldVWE\nP8SvgdgAqSH0gCm8TwdMqlTAgmWD8Fkri5r3UaaeDJcZ2Xlu4W1XWsVw1k8x\nQbyWiLd0Ss4Dc874aNLc6hzkMn+iKZjhHxlOlKiGUU1se+v24k1ocM2h1BUo\n0kUdRNlj3fIz6U6RSZYl9N+Dn3YL7ttSDmqU63YvQuISCi+jgyxOI7CNthlH\nu9C0A6MvuaRA9QOW3pUAVan64OnVroRnmFI/x77dKFVpvl1u9QqgUePa1Gro\n7ZddyeQUB5frx3fwwaUKmGPHvD8EbpHTpsNTbuyJjAsUHhGUDN5sTuEcNj7l\nvNAm0QHOSsJ4k2bPuikD9Z5IIaUtDlG1YnXFJhGfkDNv6+0f8D99X57GpNnB\nCaeh4AUj4RZMb3E5LhXVM2F6TsHZHkZxCTRQM5qWKa3RrgMhG0NjO7W770rC\n/d7CtqtO2rHVZJ6Ywkr0gfItqtfhuiCgBcZf19rv3GqDlFRypU/8dCa7pJ4h\nZcgu\r\n=SZKb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAa4RCjdrj5s1h5HnTpIfS486gntA5yaWzKTe00N4R5MAiEAhDGR8hyzTCmCSiSXr/h0NUwYaprsr8mbXPmhoP5o4V4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.4--canary.163.97727bbf6a427f7ca1d6e1f2bbe313a261e592e7.0_1627392508690_0.4614391927746395"},"_hasShrinkwrap":false},"3.7.4--canary.163.07419654235526ecf398d0d348ecf4e42c2412f9.0":{"name":"@sberdevices/assistant-client","version":"3.7.4--canary.163.07419654235526ecf398d0d348ecf4e42c2412f9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"07419654235526ecf398d0d348ecf4e42c2412f9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.4--canary.163.07419654235526ecf398d0d348ecf4e42c2412f9.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-0qVy1LWek9imL1dm1mlxxHp6SVztdX93XCz27N1NRU+CgOWaqvXG9l3Sby1G8YCt9uFSHvSWOvgsbxZu0jYUdg==","shasum":"6a8ddfa8400e1ceb6dd3349bb539660f995d5e20","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.4--canary.163.07419654235526ecf398d0d348ecf4e42c2412f9.0.tgz","fileCount":72,"unpackedSize":1605722,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhAA0jCRA9TVsSAnZWagAAlL0P/0Xvp/0mymbMyOzunSYH\ns5d16oOok4cVVE16Q5y2kiA71jwPVgj7pY1jw15HV/vFUfZQVfEfrQrbTJPf\nsl2tviOXWcPV2VbqPaIjSjra/hozo7JUeMGF5359HUcpfBC0tlJRRYnNnenQ\nnJ3IlD1C32qnqcTx71o4M3h12bulRMLQBPZrB8xkd1JvF9+OCZ0u0eXQPVQt\nm0xT4HyuyMAY4Hndoe8vpjsh9lEC6i1jNY3T8j7uDVbooj82P3tsNQeDIq2W\nHgg6xYN2SqLzWDu/m0gj60S/r7ufziPfkaZornVODrWJ/Wv1kHYwbhVYwwo0\nF1+6jM0ZgsBToSgUMDkUtg6bpfk5Ynsbx5y8HgybQt8B6ShOlhc5A8OMCVvK\nZ4tcmaBVIC4C6L18qQFLXEnOu18p45iV/0rHnZI7FERrpfAWKqp/Pm1sDR6v\nzWVd2Kqbp27sbBQl0Xh3HAomToN7Ab9Bns63ZE4wSFPmZ7cI008kwVPZlsAl\nHzgY+EGJK2x+BjcpaTRHHHwF2wgsG3eFYCiL4kSaKoXxJYbeLBkkxmiG3xXH\neTpGvv2Hau1pXN9QdytTcAMAgtLoyDbesgh66CCDDv5vWW+l5VxYd+hc8JIA\n1SSRtqQ20ykk44MOG8XYbvznk1mw/ShloKYTfBQj/ebJFTZxdqQFJ818y8NZ\nT5r9\r\n=vpm9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFOfFV/uOD2yVcO0iPDi3An+1Hr8ESXxN7IqTKuWh0jaAiBzdHoL/BmB9SlS7ncynG4VSBKbzE+KZbqIwN+8GZbGgw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.4--canary.163.07419654235526ecf398d0d348ecf4e42c2412f9.0_1627393314898_0.0966791339266364"},"_hasShrinkwrap":false},"3.7.4--canary.163.20f1414cceb19c771fb19fcb764c3551c80f7e87.0":{"name":"@sberdevices/assistant-client","version":"3.7.4--canary.163.20f1414cceb19c771fb19fcb764c3551c80f7e87.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"20f1414cceb19c771fb19fcb764c3551c80f7e87","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.4--canary.163.20f1414cceb19c771fb19fcb764c3551c80f7e87.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-D7HZX1N5TPm1Kmfeb52dDWdudjp0mvRlg6Py4a3+3SsNG4TzSlaWxe4f9/NkmaCn0TGG3ewLdRRh7HNUIJm6Iw==","shasum":"5a195d3c84645926717c8db710013e41822a8f12","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.4--canary.163.20f1414cceb19c771fb19fcb764c3551c80f7e87.0.tgz","fileCount":74,"unpackedSize":1606237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhAQJPCRA9TVsSAnZWagAAQbMQAJzXQ7CNGCP2ynPX/G6A\nE4TC9LYraynx7wPdFmW7hP/Ht2a/VO8AHTawFvLgMGtpsmYCPWcWXo0pcGoo\ngZkjapdv2HuXVBVz53UBc/gdZ1XFjnsAAUGOuJucRCOjwtNU4oe/z61G+ZIL\nhVjTQAvec6xW0ma4ndO8ZpwS3xOv1bDlUbzghPXKOvwMpTjP3gYJtitsBr7C\nOoVaXsI9QZJr37w0zoBYHlP1jb8VpZ0yVwwnGiTOop4XYAm8BzJ20YKwiFaP\nk29s7ke3U2ZwJbKBhyds6sGFy6PmXaxOY9NgLlxnadNrF5TBFCtkBeaQOWne\n00kY9VUsUP4uqKyEB030pb3wtfXehgoF1UsGBhYFq8gqO5NhnjffQhksLh+4\nUe/g8zCpFg5wKReodVoY3P+EBG2JcQmiUymFNS/ASWpB1TYOPqqKERPNgd23\nNB8MMNRHFlQqjn29VxXmoRYtL6+8KcfoVnbNd1IgOs2RZsJAmhikz5/8OnX4\nFE0FcLjZ0KyjLHYZDIoOHI6O9c1jff/8h49Q4Y4z9blMsBx6cR9VaF4jzkwN\n2vvnzingTEr5hDmRTihrO3jHniJDJdkYw17Q8mrAqBAW6B3uA+uij/Qjs0Ff\n214uZvsm75MidCGCDSN9KLKtlV1aGp+SET2TCdHgrro2ykQJlnXz9qtwNtz8\nWPcH\r\n=17vM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbqJZqzTRDbRfKkiijXCwqOrxM2jhYYmlk3VsiA5BaIQIhAIcLVVhofIPAhAZdJjzS4LJMXzmOetH5bupnhUTAQFcY"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.4--canary.163.20f1414cceb19c771fb19fcb764c3551c80f7e87.0_1627456079291_0.1529371426238133"},"_hasShrinkwrap":false},"3.7.4":{"name":"@sberdevices/assistant-client","version":"3.7.4","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2fc897aa1522914032075f293b57c9257419c68b","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.4","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-poGBmyeiBVQ96igCJiNBJE1Dj1e3RopWPYdvhfAw5Cjy2A5Z6EbtKCQKRJi+3vSqI1E8th28jEXYRVR7vyaSPA==","shasum":"254ffc3f7c468cdd05e38ee057bf506503c8fb42","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.4.tgz","fileCount":74,"unpackedSize":1606289,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhATZ9CRA9TVsSAnZWagAA/asP/R+rLODfUf0V07WgReLi\nT+JPUxVVBj/Z4UEoyL2qr9KErnuIuBdg9h3JYThXbNUvJj4XisrPTDGKxbIt\n0I5DagThvGmttK6dum3uBhPzkkqhkl1dVkPIMG8MCvA1XNvd+Cpy4AeL/8jz\n0pZ+Bf9NtcxOXFPKIJz9n62zpQKcmqrnfoHDB+E/cdHOVUwzriwzSzxbUNjm\nD73h3TE4Uquqdu8V4PfiCIuFbmh/B1g0v714XViCG5lhASIppzROqoYIRlCj\n/PwZ6lqGA4adcecI6Rf3YnQ3FUGP2eyXxIMIQLXhzSLC+L8L5pV1ySedt3VL\nUbZ4Fgwb2woOQBwE6z0mAFlD71tBCXzdHIHHn3O4u5Wr+muLwrswuY+CmR7u\nAsqIFPd7al8ywbiioePi75xOwiv1jv11seo3jQMqKvTc7Im+7djwdHx0+RTd\nuYiVeRxu52A4oDiiY/CFZ0fFotbpDjJHkpJGyAzd0adAGACGE22SwuLW4sR8\naEwQJcoNJGzkGQLIpKOrwnnPI6BKjdMUdTpgBG51Ps9Y3/03LWeqN+Os6P9t\nDC1+d0nXyUjHTmW25JjrnkscUk2fqPgTQjG9rhAt5h9T+h+kC4/Ib4a7gywJ\ncsEc8+Ah8gSB7gcIIDD/ESi/wK0W0rVLkIpRytNqQC/KmJZtI/AWFU8MTgyh\nWJ/s\r\n=Lg6I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGpbwrChhYx9WoIBC/C5nCiXtUoixD5LayT/FnUzxUTbAiEA3jRPTzheTS6iwwa2KWmm76Gulvoz/wKu2NAzo/PmXtg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.4_1627469436928_0.16400110358551556"},"_hasShrinkwrap":false},"3.8.0--canary.164.5ff78a99ab4c79caa3ed2fa467e9f2ceadbed313.0":{"name":"@sberdevices/assistant-client","version":"3.8.0--canary.164.5ff78a99ab4c79caa3ed2fa467e9f2ceadbed313.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5ff78a99ab4c79caa3ed2fa467e9f2ceadbed313","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.8.0--canary.164.5ff78a99ab4c79caa3ed2fa467e9f2ceadbed313.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-m6uTzU6iGWN4/kCt9Cxt1ljrAC8PCtFTXIdwobxfCK64ft7Io20n9nJ776wlxYFmowjZ188nIcMJGtROx5N/Lw==","shasum":"4ad14ab09d53659a706c651c77d76a7f2506fdc4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.8.0--canary.164.5ff78a99ab4c79caa3ed2fa467e9f2ceadbed313.0.tgz","fileCount":74,"unpackedSize":1607032,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhAqDNCRA9TVsSAnZWagAAHMAP/3eBoxosdJlOUyr1dHSn\njqlcLGnikzcuJHDsu+m2KL7betOKHWiwot747Ef56GbRzMQQkZ9IESNtNb6x\nu879DE1IeXbt6QZHCzqi5bJCfKdf5Qtdv2lTMlfbGJ9/LsN4/5aALdQgnNYk\n2+xkCuY0zR+e/bWxg2Xr0MVjIumHBXSZkEmRMTF51tHmS+bc8tUBB1G1remC\nsXLOPdvZ1/npCYVqB1ikvkfE7Vfq6feMfLBJX+r34LVn74J2rD5UZ5aef04H\nT1BnR/G7LQwgRxc3OXZHuzv8MrNYVrfUYM93GedBAMtcwbdl768XNfu3mU1h\nOEq3QkDHLfAc9RAdh84ZWlB5j4DjL0g+e6I9cM+i6xIc4pLAixmoTDtw1TL3\na3ZfBghCK6RRN+o31N1x4ihpmbiiU8/eyHshFM2XNjyo6Sr2gxMt2ZYaRhCk\nycz4vlfEgNjwEJZD2fGNHfz63q1FjqRwgVC9dZaA6aTamWP5dPKfkirOVA1E\nqgxOAfYfIbwzt4u0kZLa8ax0oUG3RazuWO1GoiVweWgtsdDXc4hn/XhCDbcc\nfVwgbbeS7Z1NT3zGPY9xEDFbHiFUNBrwCN9DhKPDkudrciCgyIxQBl4DtHNJ\nLwVaxb1aP1H2jYU2AK/xbthOoqqZlEFfITdB0DYEZlgOVdpC6cRtjXXs1Jwy\nQiRx\r\n=N6U0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC+1FdCpcLV9Eb5Qq+oVJFB6LnGgGdCZfEh/ZZ0lX9+IwIhANAm6Gn8wzexqMv69eRRpXgxP2mpIws4ELGYTxkSJi4Q"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.8.0--canary.164.5ff78a99ab4c79caa3ed2fa467e9f2ceadbed313.0_1627562189526_0.0022547087113302666"},"_hasShrinkwrap":false},"3.8.0--canary.164.2567be318fe6cc6022674c270fff8aeffb8c3503.0":{"name":"@sberdevices/assistant-client","version":"3.8.0--canary.164.2567be318fe6cc6022674c270fff8aeffb8c3503.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2567be318fe6cc6022674c270fff8aeffb8c3503","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.8.0--canary.164.2567be318fe6cc6022674c270fff8aeffb8c3503.0","_nodeVersion":"12.22.3","_npmVersion":"6.14.13","dist":{"integrity":"sha512-4edBSVd/kCTr+2xZ0jEEjX9Yjp7NsclUBzp4jZMT628juM4YxwXPzN1RkluivQhy+TG6Y5Yl8OiuC5W9soch4Q==","shasum":"ca1aeb5045be582e1d2262fa5ce87eb1ab272cd8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.8.0--canary.164.2567be318fe6cc6022674c270fff8aeffb8c3503.0.tgz","fileCount":74,"unpackedSize":1607032,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhAqROCRA9TVsSAnZWagAAhmkP/1Cw/VlQMuTBTAN+bWKG\negX1O5fxQcILvsV/213Qyp5pmq4O0QiAC1jsCG7hKyrt1ORqXMQT4jFF1Qch\nJVnRNrFrzc5vD8IhXev00iJdHMXTR1BTm3mU44yiJvRGnZsVoDdHqpQ6u/kE\nEWgpCbn2OAjeoJpEM4TNuOxRRjuEdJy9CaXtfIMvdc6JrIML1GQ6YfSZFX0+\nbScPm5hyWWw/HCrxJzyTHmTqyabm9JL0VZqQTfecjO9ECeqivGFY1DLmi0k0\nXoGoabnRqiP/MirVOAnmjeR/U6nZjzsXIJ2sHYFFzBKu/5/uZnWCTchATVrg\nChWutgCaqtafeTISWDI0DSW4Mcjkg7iUmq1JBHoeOrt/iJ5ugLg6KMY02XF2\nm/gx3ZB9FKIjUJmma3XQrGwTJ4DAx881JayEsSc64oZYZram3Uzkb0NJvhFW\nYBUwsLjihrOPf4XXaPwDMul/1uG4SNdYmdriEslmVy4SdFr7MCBARO/FCQ2R\nv6MbVoKJEA/XHG/blYgDHD57d+Vpyt9kVuVl5ns97RrJ6weRlfZGRkXxYoPc\niCjFeaoCbjzLHxqgonZAkrszDxXhAV1SKyf0QzkXt8MPbkYrWlgUs/6+wwst\nP8LTtPsVBLB59RAr3fIkd0Y+brO3Hk5ijwXIKXUljsObZiH4qTsoxul/yJc9\nZaQD\r\n=mRsW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQClC97dr6awUJyBX1b0QEVnAMYJgmts5T1i8tPa+kdvwAIhAOTBN/xrA/0GHkylJUimKeHzAZ4onNdJue8epVI3RJX1"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.8.0--canary.164.2567be318fe6cc6022674c270fff8aeffb8c3503.0_1627563086096_0.16394344467564337"},"_hasShrinkwrap":false},"3.7.5--canary.166.0ee1dcfdfbc9353a1a7bf3c934ea0f6ee96ddbc4.0":{"name":"@sberdevices/assistant-client","version":"3.7.5--canary.166.0ee1dcfdfbc9353a1a7bf3c934ea0f6ee96ddbc4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0ee1dcfdfbc9353a1a7bf3c934ea0f6ee96ddbc4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.7.5--canary.166.0ee1dcfdfbc9353a1a7bf3c934ea0f6ee96ddbc4.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-rELnGdjXex82MMpaXDKCLA8KjszvbhZLlUN2+NWrxiPUIYE4XtH61RThbxmdhp5SsF5aNT0M0nUwauiqnwaEJQ==","shasum":"c517cb5b37ddb773616e52bc5845db17d5ee7a43","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.7.5--canary.166.0ee1dcfdfbc9353a1a7bf3c934ea0f6ee96ddbc4.0.tgz","fileCount":74,"unpackedSize":1608674,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhCjnFCRA9TVsSAnZWagAAph0P/0UdWm6gtLFwr23Uzxc0\nPIpyRzsSJnBjT4hd1k4yQOqz5Mds9vq1mUIEGVeTrBphqQrqymJ74TQT5mmZ\nhOxodtCZVrUaeJEYFSSv4iWH0gkbofs5raMHPR18iWBkgL3ePo7am8E6oQtm\nLfC1cjtU9CQuTo2/fZJdl1eD9lRCG7ItTuqy4A+nGXQU02MGC9hSA7XTc8pV\n+Tl3F7zsRrkdM2D/PcDk0VCoXOdWwQheCGWvUU0cviaajeDNQJdEs33qsaU3\nqmV6Bhz0yjjqyHsLa0YCHyTvn4upSSTk0p+2vJ0MDghJfsugDdlJ9bsVlxJc\nm1iwga4I3VnTzHHuiWJy0niyfkOq4Ni/1XCCW75GC04CwsUJifY7YPuIzsFh\nUV0LOuTcbsSJBg4VX1Pi0/UsafzQC6ZJ+LVdSFdGUgoGh8XpQCCjbuv1+bCl\nkn3JFu3AhM5fNLK5E3TDD8tZqubsWoVqklfvbnnIY/z4en/F9NBT08gSpGiO\n/Bd8IP82RKCK29Sc0cX6WJqjvqNS0rGxiAQtLtVJDxWu/a0cQuq4Qt0IrkQo\nLW5TnNRH9ctaI+2VfcpD8EO4OO4s0xRg/LVq4dVVdaDyCXl1/VbqJHOR+dIO\niVmFmzPNWhziIPdq4nWNDjhylSZPSnCPOZJrsYEc5s6gh/gSvLlADaQgg1QR\nTYDu\r\n=+YCH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGn81B/oMoD/e/5EeNNb7JqwazZgE/c+0wFIYlGJhkB5AiAxCZ0rGvFRCezKhGJAIJSxrmSau5jTMXDimrEnJ9kvJA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.7.5--canary.166.0ee1dcfdfbc9353a1a7bf3c934ea0f6ee96ddbc4.0_1628060101554_0.19365605168278788"},"_hasShrinkwrap":false},"3.8.0":{"name":"@sberdevices/assistant-client","version":"3.8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3fcc48d5bb91dd792149eb4866a07378d7322d9a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.8.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-938dZgSGQ2Swu5edqlEBQPIEvgpWs43oVaV84dRfU3mjVHjm0lOJkkJ/8IDqlfF8k/SjLWbg7dva9GznTTnz6A==","shasum":"3036f3763a7d38bf8ca8f782333d313004935254","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.8.0.tgz","fileCount":74,"unpackedSize":1607105,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhClMiCRA9TVsSAnZWagAAb8gP/R6XLwCR9q+aNawZINGB\ns+8HDhJnA73O8mpPoF7rWWfyJe/8sqHmGgxoOrszmWZzNCrBAB/uxRC8fMJY\n0XoWWbCww10jTsj+nV7R33z/HLWgkKNDjVnFxiTdNyR0GbIyjyHUYGugEy4g\nVRO/R6zUYmbMwLZXCQJx436GeVvgOsGc2683no4k19fRlOlij8Pfz8J9Y4gC\nvRRg/QWHOQ5ff4iMtIRSVzY4fNrGOrEbecjKPyxsve1wf9eAlUmgCZvw2TZ3\nXVZFSj4Z+NaqDf5ttrHHIWaxypUDp4p9sX3+uvQBhZAQI2sE5C+zQJa8vKnk\nVLwhPdWW77covDOOfKYMCs29L/fx57/yY0Fc1agUzKW7tYvrOrrz/g79Eniu\n+ECqPltRdxreQAjjz0V5OSiWEXsJ+rpvqX/StejLgCEAvaLtF0k3O350E+1f\nifYa2yJffjBVGOEVvNbvFjL3jdMowz8eu3+sYTbyrHL5GWuuu/IpP8YLyh55\nbZg30XSvj6T2u3LnOcr0gVgQPvpUyzGeV6yPSt4d5EydjwmQ2fH22oFdjZWv\n2bKOGFnDUrHarMsxvXhoVBWVTKTUig7TiZBSPFAQ5klJPlBYYE0XV4oLOmc+\nSiE+6EKQ+a2ojHX49vkGJ+uRmOfJHEqOlUjm8IWBpVM7XvgHG+0EXBFXJsVG\nutGN\r\n=OoMB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDL9vwI6d3PO8FdcxGVy7+fxLiJDXEQs5BXKLL4PObPyAIhAMzhc0HnB7fHY+7vEfavcXBXy58w0FgGxrL12taBYgXt"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.8.0_1628066594265_0.5824007605505637"},"_hasShrinkwrap":false},"3.8.1--canary.166.52fc3152eec35b364b5cc01a3ebf7ea1ab76f05a.0":{"name":"@sberdevices/assistant-client","version":"3.8.1--canary.166.52fc3152eec35b364b5cc01a3ebf7ea1ab76f05a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"52fc3152eec35b364b5cc01a3ebf7ea1ab76f05a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.8.1--canary.166.52fc3152eec35b364b5cc01a3ebf7ea1ab76f05a.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-bO7Rzs6UD6eFHJru1siCGAcn9bFFBsXW0LGDBobIFlcZZ+OxoMCuAFeSYg1IzBn2s4Q/3dFDJ3FWbdrClaYdLQ==","shasum":"8edcd3e08821d0b32591a43a2000b2563a979b9e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.8.1--canary.166.52fc3152eec35b364b5cc01a3ebf7ea1ab76f05a.0.tgz","fileCount":74,"unpackedSize":1609490,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhCmy0CRA9TVsSAnZWagAAGZcP/1t/yMUD5QFJXpibMl2E\n/GsjJ+1JjbycNNwSJrJnAh50spV2P9dKutW73ErjLPM9fASr6VAUpjQtlP5y\n+P6666x5yJwruacQf3b5bItiRhF0ds9sFAzL8BwqziwJRFUU2IhSuOiOlJmx\n+XRrtGMprSaRyXRvMbsQ3lPzqIQU8vMzlBn2Ze9Hx11ZU0HBP/Glqb8PmVXz\nRdvxJMP1KcyWfPGwNB6xogSZ8OHpeq50pTvHV1NL2cHj7uRy7BBpKJldcfGc\nUxDMOkDAu8z07Pu/VxmPbwVzBJXE/CUtkh8nfT7IikAOCH30QcgheL8Y062f\nsePHAkgX6hxs00Q4mviNPzg11ecRbX5vGxbD7JfMpnZ2vRHK13you5/KaNBJ\nj0VQhY/Qq0SuT6Q8e37uXumCvDMMgdS4rP3TQKEZxXCyUaTrp1EBA7tfvf0u\nNZUjqsDMkD313c9Xv6bDnEHElVEdFepx+xD6KY/ptWTB3xvY18JLo6703Huf\nMjvXoG4m7U3kHEF98vY3IXylgq8R37yg+uoXBwqNgZrB+aHDyU9kx6odJga6\ne2fEBPrP1J4ykXbwUolXLe1DbBBnje+EQqYXhk6Y4VGIaNnd4X2v0zhrc1QT\ni4HHyPlE5MUobe9pqxHccWhYPFJEJDpI7NdjCYAy4DJGfzwkt5n+9EkxQjlj\nwgPt\r\n=lRYN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICxaJkD8Z3NlDpoUX+JelzhFPnJrp0UPLHAc5mjfqsaRAiEA7LIJkv9vA6hahKzfRZdoTsGJQQzP8KEHlmQkH5FK+WQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.8.1--canary.166.52fc3152eec35b364b5cc01a3ebf7ea1ab76f05a.0_1628073140710_0.1886613149219658"},"_hasShrinkwrap":false},"3.8.1":{"name":"@sberdevices/assistant-client","version":"3.8.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9c4a4e12ea4eee740a274ca9c0f2c0a6dfe5759f","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.8.1","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-T8RbKxL6dfBcU3dSl3hzQIQ5X+XRwoDqCVRMRCrWTXP6Mh3JnQ0GO/0QXcxTWHQ7OWjukglm/sVxvcx8qB46Hw==","shasum":"c45095b594897c024b87ed1fb861c3a10e8e84c9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.8.1.tgz","fileCount":74,"unpackedSize":1609608,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhCogHCRA9TVsSAnZWagAAZlAP/1PWU4bWx2Hg0U51frKk\n2koR+7bP8OmNGCukmHHILDldZNkkWWJ4kigNFiUzWhGAG28YRCNNeeHrC3g1\ndjUr+4Ofr1O4FQEfh7sgCcSc2HwomgMTo+Vino1SyqOfmmstIlWmR8yaTG1F\n7eGyh/yCVqIWFLHbhY/sDJECDO+u7ESrB9BQvgMBgaiNPHv4T8qItHOmwQ3Q\nc3tvte4vXjSp31WFODThck59FoNfnaGzIbUaRjvQRBh3C1cE02/YzH1KBEZb\nDwmCQTfzca0BdG7b0KvivOIYWynPGDR3fcomMiunHbZ8svXanC3RaCuipaB7\npxp6DxmR0yd2R8uf5hIwdr+CRPkBOZ6HTx07EYd554nYIiNXhmX0VXGTQkfX\nMhzdnSukTVlwGmffXdR3J7vJSrVlkyWK4tDaHbhqib640jNlZGVgaX6DQG1m\nGhzQKCHGhue1ExwwLChjTOfC8+9qrC1iAJN5zGcWIOqi/kmJc72JDP/onH6L\nEF0mO3NYmIMoMYQo6gIelTvOiOQC+PRuDuBw+yjCkbfL04GAZQty3bR14Res\ncA6zbnNVeAtwvZVNBvXDLTHtB2KAHgvkfiFn9PK4AyCXgw92NdnNBr0IWatY\nYgEV16Tn8A7NQmVikTSrxp4n3CKh/9ccJO0JqPlO7nwbXxQX5QNKHsfSCaii\nP7Ml\r\n=tJh3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWj+a9oAkGKveBOYOTZ2LYB+c2qhJTiqqYsQLTXxXoSAiBMzEYCAJnAeaPukBj8RF1TSt47yVxkObidaU1n0D3yYg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.8.1_1628080135421_0.2218330800662336"},"_hasShrinkwrap":false},"3.9.0--canary.168.c54879dd63597e6c503969197d21bd21e4699e7f.0":{"name":"@sberdevices/assistant-client","version":"3.9.0--canary.168.c54879dd63597e6c503969197d21bd21e4699e7f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c54879dd63597e6c503969197d21bd21e4699e7f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.9.0--canary.168.c54879dd63597e6c503969197d21bd21e4699e7f.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-KGhyg66QNNeyQEVG4ifBdLNT1zrpJI1lWxKV0+6Mv148e9FgbCKo1V7svexwIR+fg7kXjcwREgscLhiDWQyhpQ==","shasum":"36c0eb7354d2d3ab5051d271d3873a468027a55b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.9.0--canary.168.c54879dd63597e6c503969197d21bd21e4699e7f.0.tgz","fileCount":74,"unpackedSize":1613686,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhC5rpCRA9TVsSAnZWagAAYMgP/jc1pkaIEXLdQ51NVmn4\n2Lz191jgypEdXFfALHnAwGMZlAgNRx8HbNPjvqRHLRmBiD/xUpq1BO/pnG4n\nCY5tJG0bZaUITKjoOJ/qhOaUkoWTBu48DDTkrs28RkEFQmItiIRzHKrMAPoE\nlYzzXd50rPsgzEyDABBh2VXaUy575lOvEnlzzrHEUFfbSb8+0/bdo8nV/N7C\nfBU/8e/18kNsw2zGJdAwZOOurLrbJwSBVnGQeKmBYCGpSoBvE4dIVbZlAKai\nSTDvAXlhK/veRfH5UUbhWgpkfy5hrMUlI+hd/QPnyqyp5cXcZ/hEX4vlS3Ks\nkj3ri3NRiFGzkzctD8bts7Jpcc7T/a4L2RqzhtrdzBGmAUobcfk39X4NCSze\nQG19m3wnPiXmlzNy1QtqrzsHB6pwRVQh5Q22+PZNjtVSlQSOgK0tYOY94REv\nelYWgG3GMpcIk6JLx4xqTOj6DfsxZm6VqA35ZKVLw4Qn6SM8LrIBI/Q6NCfh\ndCAkI8WlYIl/y/hwLap2dNp8vCSvhV9V45huMesBg+hQcTW9//7OQmvKkWI4\noTzhrrfaHlLvTjT/39or4Rki88Q1u1XB7NLwNnv9TtJlxWF9qTQLtyycS8gq\nAlcFKrAj3oSh7z/iSwVi512TA9IF7jn3y7/SuthtO5Rdp4nptOf2fdW132WI\n7L3j\r\n=Q+Eh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCzo+aWMwedgaWcKO7pTthtIEHSdh1b0fpnG0OH92odpwIhAMk+Lhi14S7kipOcU2c7Olv4RFzRUtkyKUX2Bg1gARUG"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.9.0--canary.168.c54879dd63597e6c503969197d21bd21e4699e7f.0_1628150505009_0.3714415024643136"},"_hasShrinkwrap":false},"3.9.0--canary.170.3944ff197944f47df447daecfc345f4067c228f7.0":{"name":"@sberdevices/assistant-client","version":"3.9.0--canary.170.3944ff197944f47df447daecfc345f4067c228f7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3944ff197944f47df447daecfc345f4067c228f7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.9.0--canary.170.3944ff197944f47df447daecfc345f4067c228f7.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-yxbA0rGfj8fI5tMjdid7yZyulGXQ4qnyRJuWQT7jqtRR013vPlfMOzAD95qu7KV7GHD7hoBiII9UOiwVw6FUZQ==","shasum":"3d152b9dff03801f088b0a6c88a0db84ca800cfb","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.9.0--canary.170.3944ff197944f47df447daecfc345f4067c228f7.0.tgz","fileCount":74,"unpackedSize":1610167,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhC+HsCRA9TVsSAnZWagAAVVMP+QCQB/iD9qjnFEjordwB\ncScNc6TY+Rk25M33pSi0Nf5SPrNVnu8D4pwyeJvm16skaoPNhGr0lfXOrX1K\nmm79sJ2Wn8KcaiPffsCVgtZkFy5a+i7vaN/d/UmCPahUef4hl4QPvYdZGj5V\nd4QSAo87J9xY9dGkCEKB4AyddL5bTmkelzj3abint0oCE9pAqR97HA/+65cE\n6NGDRrDw57sXcFdQkH0OcDYhQvjjhTUWEE+lul+xrm0Ic2/H6PiQTBOkl2Q6\n9ugHgXHO0z8HrHXJss6DrMnpyLybfqRB+z2hDOYgEqUTVYP+l5d9fZXOU0Dp\nltpdi1pzJEa3yu/7V72bDXYwEY7qBfh999kBj5/AWbp23qs/+4g7mXRU2cNo\nNTxm+Pecv1ZX9Y1BQCR5yb70tfTZLcQzyehkjxyvyfiAbHXQXa14FzZ8PfPJ\nWb7sr57oz6Sgmb5GaoJHaA6LON2xcWPBkpWyL+BwRbjbV7UjMQ4wBa+mGX+1\ny5K1kHWLc5w1gofjEn0MDNEU7G6g3WRh4l50Vf/oXGi+E/XpznnellVy0SDF\nVwGbdOfgyOE3wMAq0SpwJgMXQswEpEsSN1/KBb2fMZgMLqOL7q4rz9DUHyZK\nImMlYFHtSDFESTt5+XEk27hUuy2DcWFPwsIsdEp0pPH/3OYzLsCOLRQpNmP4\n87/b\r\n=HiH5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCOwZmWZP7r0SYrseoU/M9Ta1cgbjOVFwbCM3hTmqO1IQIgXcEAJamBaZwkMURWBez2Tnwd2utLOsFq9OBVBlp1BQ4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.9.0--canary.170.3944ff197944f47df447daecfc345f4067c228f7.0_1628168683914_0.2032863148318964"},"_hasShrinkwrap":false},"3.8.2--canary.171.e6ef194c9165612960bddfb0dbe8cf0fe731e718.0":{"name":"@sberdevices/assistant-client","version":"3.8.2--canary.171.e6ef194c9165612960bddfb0dbe8cf0fe731e718.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e6ef194c9165612960bddfb0dbe8cf0fe731e718","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.8.2--canary.171.e6ef194c9165612960bddfb0dbe8cf0fe731e718.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-T9xt8tvR6IqGjPDayULI9sdaiZx5BylEIALNTDF9iFCJ/onDhdUF5+rDVBPUm/KtR23EowPWm/vcokzB9JHvlA==","shasum":"d56ff1803f0d9013015fb6ac252f400178d1d3fc","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.8.2--canary.171.e6ef194c9165612960bddfb0dbe8cf0fe731e718.0.tgz","fileCount":74,"unpackedSize":1611559,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhC+3qCRA9TVsSAnZWagAAOHwP/159fevczjvuC/7nB3S3\n3SEY6DnKnFrOTJfAWCVD5aRvlndmTP3cVpPEpEXzDBdknD7ZoPZIpJNuza3c\nI0iS9iottLjhTKkPigmKM5BpBWu7WoLqagbb/1cLXZbOx2tgzzZmz1GyKvLy\nhXErAEqN99tQPXdEpfvRw/kbYaKeDrdOTAKlKtZjJdn6PFXG7iDFIhBJlICI\n1YTfszQkkR2HQDhJMeI5cOfeTRjsTczDseyqD70r6T0pXkyoiPA8lTf9wbYg\nMRduPJSwaPh6ITCYc2BuWHYPibQ0hQFH0WP94Pzm+oAqSJ31yxjKYuuMHKgW\nzg9HfUQQOZQTjlgMJJTSGJAxZHT2Ixd3jUMVgnGfd1JwiPvqD1Zl+Mk46GcF\nWyiIyftoVKl2RSy2/pLl03IkXwoulHXz3q58iyHvpXcFFF1n8aKuxOj2zSKp\nuJsTA8pSF+Hr1CVw6Yv/iczFGrcTbzBjL374cxSMd0XzeO+HyM4nQKw101ZJ\n+j8nwJA+gAUPXjHLvM7yhUgrFpNcuDHTC39SZoOZCJrg4rBubuN/xYcUNAkO\n9UfNwp3/pX0SG/VBatq+CoJELEzdmoSbu1UMCY5CPqwOExVJbUeLhcQuG08G\nOblhWZjKYC3EFVnetBltGcShKo10GrSUoCGi0BcKqgHzfv9A62sefC+rPtaT\nS49x\r\n=P7yO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpkvLul+3kyk3M9cU/V8+my7IRK/yXano0rmyZOmhVdQIgFzXij3k3/9tlHGnE4jfzheYP9e+gYiIUJV4t0BlaVuo="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.8.2--canary.171.e6ef194c9165612960bddfb0dbe8cf0fe731e718.0_1628171753769_0.11525642228700095"},"_hasShrinkwrap":false},"3.8.2":{"name":"@sberdevices/assistant-client","version":"3.8.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2e8c3724691a095735cf7918ea89c9a74e654527","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.8.2","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-cZA4CPiTP1zE7SVdfhqCkP8syxATM7zZBnHcB+ohrOuE7rDa55NiNVMxdygrrAXPo5WD0K3Lmy/9P56G2TX25g==","shasum":"557bd94b6f7abf19c47f1081a6835c34b6004d5c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.8.2.tgz","fileCount":74,"unpackedSize":1611629,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhC/vACRA9TVsSAnZWagAAahMP/3uSLkk0jHR88fUeGThw\nkrrMYgSZEISNuFTrqPxMP/3Xts/wC6E5Gp6Z6Gg2WxR8dv2FNgspe0WdCrXH\nsvko/6nVOzhokBKun49eE8H9WhP9XjsSohg0eA6wKZd19G5mRfFrHyfwQHoD\nkHapYAVqt4ppmP/OxAbt6z/s7y7Fut8eGrDCMq2LW7ihLYEyDWoJUn6r8k7i\nRrnf6HdTteaM2oitEjFxCZv9zKiz726cgzCPtz32JqL4TTXcF44SYVXnvDiU\n15sSbYGxE0zYltBd0RnbvysdnaJt82tMkCYIcvlcWon/x4RmNGtUmll57kTh\nJ6H6vn/f49uSv9f2QWDyo8OEABrjXS9mVW3VRHuJMrBwzPLaVwK+5c6t5M1x\ncVrNgHU24efkgN4HnX1Gs5OjnCZtKMoQhfPsCe23BLxEzCkFb7f28DS59F1O\nKWO49VYWQfIvHStT8/FFWh2EdVHsmBCpdPucvkHbOHebSYWeG9Q4MFCfwe3W\nYLcFuyNJguPy9nHcPk5JnfkjYRV43KT/CcmGpJYLUD+Rc1uzU0IhB7X6ZEu0\n52elNoqazCvJCGAJArs2re9NoL/yxaP4PC7vuwwWpQnXHEIZ7cLLAzBtsns1\nDEnYVC/6XOpNjr4kBHUH49L+27mIDX2dg81RvXfiN5yFkNHglReV7kGAjtKK\nIvKs\r\n=X0b0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAyt9nzqbo7K7BwNTcXHdU8uvuPu5K8S/YrD5LHBpBQqAiAKPJ+Y17//ztqOAhfZMT9P0JgXz8k7dwwakVXqza6h+Q=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.8.2_1628175296509_0.08699125180616885"},"_hasShrinkwrap":false},"3.9.0--canary.168.0a377c24909944c824cd2cc2f8c5c225b2748963.0":{"name":"@sberdevices/assistant-client","version":"3.9.0--canary.168.0a377c24909944c824cd2cc2f8c5c225b2748963.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0a377c24909944c824cd2cc2f8c5c225b2748963","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.9.0--canary.168.0a377c24909944c824cd2cc2f8c5c225b2748963.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-5i+qUx7m4QGWPsQznQEUkWd43AW79VMCT7LsF/fHgJoXy6i4j15CV+GpzascTiWPSjZu7SP9EeCdTwAgFPZWcQ==","shasum":"bd39238903c4c4cdc5835d649619a0e0d0170774","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.9.0--canary.168.0a377c24909944c824cd2cc2f8c5c225b2748963.0.tgz","fileCount":74,"unpackedSize":1615707,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhC/yFCRA9TVsSAnZWagAAwREP/A+ZI3wG8dR92kjSWZLC\nBzD6ST+SEvCPM9H/v5nelNnIX1x0LzsHv8kOx6+biNiHMj5kdTXvxdsgfzzv\n9SOaJ56t20DNOF6H3zZ7X0KOm+izds1XgN1cIrC1tk9JP+y7SFUFiTAeH1P7\n2jJozRVPmBP/CwEtThHHIZLR5KLWNUAGNHSDzRhdwuwuURTjy3sQ3Vc/TkuA\nNRAKU2a8CZLPnS4zEKqGGnGDLeQ4Nm16nuaddQO+h45rIIGaBEGKd0s+0pUk\nWGIEPp8pirw9rjlfpOAFcNbOtnYgv9uOitjg0C4BlsePJjC3MlP12Q7Iqhh9\noy6Her1AECwGwsEawQvvXtshrLgqc+Q8NCz5RiqEarcq9J2AlOrdwQKv75Dr\nKEu0VpXhqpRbditH6SPTo8jZ5qEpfrajZCO5RfULecOXfFghGQviyaz9pUyQ\nbeCSefA6B4a3WrfbMDJYVkCzQzdxI0seh5EKWNacw1XT2fw6lyE7tjAQFTrd\nVR9uYx+YXSraGoqqdleB+kaz5ZmtXdKaIkPWnwbHknxCErG/Yyg5tdZuxaK3\nkNzfw9WP5jT+bYdwgEc3maPg8d0+8spqct0W/G+Mm4czHmoJlQzB/Z8zahIq\nCA7lRXBoKRTwJgJIwUqfE9DddLvw/2bpZ77a8+qe2mliEC74Ar9KwHPP/3gX\n/IsV\r\n=niOM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGc1noNN+61X9xe11UiiqdecEhxqefiNGnA5WiHwGwDFAiA1iJpzFKacfuHWSq8xIkkl7TihCA4+S/Xy5eqbPwqNtQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.9.0--canary.168.0a377c24909944c824cd2cc2f8c5c225b2748963.0_1628175493225_0.518439319598698"},"_hasShrinkwrap":false},"3.9.0":{"name":"@sberdevices/assistant-client","version":"3.9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c73486a71a7e2400ec3f8a60e111c0262eea5349","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.9.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-SyQEaaeRQdZByBIoThkJuNOJYswZ9PEOQU6I+hKbQ6qb0PoFrCUTOMrqC6R6XXf4FfZy+va+o/+Q7nDbO/fNOA==","shasum":"faf2cda3f972378dbeb4b66ec8e581da8f4b39c2","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.9.0.tgz","fileCount":74,"unpackedSize":1612266,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDPaGCRA9TVsSAnZWagAAMt0P/2O17kYO519qnm3FKVR7\nhv5O5ST+YVgsgYgPIbT7eaAM7Mh4mvPxvU9DDz7VgCybpjf2KhVNlAuS/MDt\nhiLaNhcs3qHGD04n1L8jlq2dL17wvdUdHT0d1+ZjQfPucUSGzOUNI3b5Mn8z\nHmBXh8m6WIWRbmmN4LUYdbJiTaofr2PersgR6mLuA/uPar9gGMA1l+iBOket\nK6A4EJ2oEcKj9Y93J9SEY+dI3vMwCBWnyNyT+0Ekw/Csw2ikaDfWZHvD+J5Q\n5iJhg8hgevAZa+cFyHQ/WaPyaO7Gk1AKUqY76uCUteOmqUv9bOAEaqLxLYti\nITRBkl7+4bJjUNDy+eTwvOOwmwvbMAVGB/WGdgB8PrI0diYd8oweojsB+FZB\nD5hePktbFCkjoXlCKiG/qACEZdOa3bmYZF7w9bXevZo4n04NWaRd9ZRQl4kS\nNL79l3Fkf2D8nQ7aCGZKiqluIjKTZuPyy6WbYzKT+pQo154i9O0HhJuTXqo9\n2Ckc/kJDt4JaCcMvuMuzisNQx0oLTtWIAXLARzhBrcQbx8HyGJd6DmdQd38e\nLTlKdyvIFj4md3gToU7mBMTKDIZEaR4uaw8+9s3VuM4BuCTsbNNdbE2sIJBC\n7PRnvY7ijLbBD0iKl2BhOno8huXoWCLTPInaKqrCwRcowyTEAJ/ruxiBz7IO\n0tRM\r\n=lDNB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIANO59C+diMgTzyvJ4WY3vl8YBLa9Msui1P3nwDvwew7AiB/u7MyISSVc4Z3M/LjrMIyBkpQv1XKj0tlKLa1i048ZQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.9.0_1628239494356_0.195929776544016"},"_hasShrinkwrap":false},"3.9.1--canary.172.02e84b64a9d59ddfc659eae1a2e25a6fb379eed9.0":{"name":"@sberdevices/assistant-client","version":"3.9.1--canary.172.02e84b64a9d59ddfc659eae1a2e25a6fb379eed9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"02e84b64a9d59ddfc659eae1a2e25a6fb379eed9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.9.1--canary.172.02e84b64a9d59ddfc659eae1a2e25a6fb379eed9.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-Zl1Lc/VBlNLSh76LjyKXSyQQfOvwzg/NRLLfKDAdGEICxdCYteAC19rH7YN2cGkDyxz9ck5TIyV8h4WuGIBWQw==","shasum":"8e571c6990e9834ba1633dbd0e77fb48d1f15b82","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.9.1--canary.172.02e84b64a9d59ddfc659eae1a2e25a6fb379eed9.0.tgz","fileCount":74,"unpackedSize":1612148,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhEPS7CRA9TVsSAnZWagAAsPwP/RrmG8XoiR6/8INycq1c\n0L3hXzASVTqd3Ix+pQEcSLP7d6idExqrcyM57xmvyN5neEjGkE6FNga+7H1q\nZOFtIBmZYz2jl54mb2pmTXFZ7l2csq+cSuY630+BHy9Bn5cIfTY+NGcfw6fx\npXDweZrKb6WApKBk9DL1POSZQglgiWtY4DR8Y81uawio9TkbZ56BDJak3JU8\nR4QmZ0aJUkGBYTvZSwgZCu+H9gmP/mEWpUZdL6eCFN0E+yVWGWQdGd9xwoCE\nMzcXX7wZSBUi2Wq5iNL7C4nQFSJIKFq7pMnJxXM/7Q0KY3HRqCBdQLj3ZrCi\nuPcwUeNzMDBVfIE2lgrn3dGlbRxdOkNm1P9FfCerpWaBWjzXHQ0ZovxWDilw\n5UPCtqKBdQ2ebUNr8J9qXqBVIWxFnfSuWT4Zdo4xNvVoALw5CXcQj5hYvJu/\nIEpw4R5J0+PYx5TvbiupTn+2U8dRJcc4Vo4ts9Dd3MsExK1gXWXMGHMROqGw\n1xz3qm9K2AU+oD1c7lgRIA46MIgcXlD9MAw57vMGe62n9Tw+s4kwjR8nzzgy\nvzkAy89apeiiSN7eVw9TT3SCjXc+L9CrYWaDy2e2T14NAUcoN5dsJZD52x+V\nE3jU8oaQqw2clGMAEayXaggyv9MT5aqCjch7TdCsnY0MrBPsrRmIS5U+RY8T\nDIrJ\r\n=pdaJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGiJJTk3jazpDqrPhpNKJZ7DDtB85dEEwXm7/2X3cUKkAiAZLKVX9U+zB1uGyUUcJBcnyqcZiwc2lZgzePGHghFEKA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.9.1--canary.172.02e84b64a9d59ddfc659eae1a2e25a6fb379eed9.0_1628501179345_0.4353836453574207"},"_hasShrinkwrap":false},"3.9.1--canary.172.fc0512a11e792921fe1a33d0002fa81f85dcb36f.0":{"name":"@sberdevices/assistant-client","version":"3.9.1--canary.172.fc0512a11e792921fe1a33d0002fa81f85dcb36f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"fc0512a11e792921fe1a33d0002fa81f85dcb36f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.9.1--canary.172.fc0512a11e792921fe1a33d0002fa81f85dcb36f.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-zOVYCbbmVFJJLxWHmDfegwNwUPcQBsstItt7gIfO5Zk9vHxOPpyclMXzJfbJk1xRL/4nsNB8fXtGS5otwODOxA==","shasum":"0df80a78045d47b1815a242492cf26a60ee72de9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.9.1--canary.172.fc0512a11e792921fe1a33d0002fa81f85dcb36f.0.tgz","fileCount":74,"unpackedSize":1612148,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhEQvzCRA9TVsSAnZWagAAWeMQAJU2HdFDN7xGRhpP4H1H\nsNS/8zipaUf2ytdXUoZNG+0oLHeblUoSmwop/Oy/vHkEDD+TMU4mQmBHoTq4\nY7X0IpWUavc8LUExdZTKh+2vfszXai8KwoiujKqAPZ9TMgX9+fSXVYPCpQS0\nhs5WFuII0EHHotvIrOTu3Us7cYPd8ZiKYNboO6p50i5OB6MSw/iZBPMR4wzV\nkkO3lBpnufxKCLVSWGWUHxqeTnLDNBRXS/90sog5RcVUHnYKcXzmXpAPczDb\nHgJr8HAuoJwzLcA+OxhaGX07qYEl9HibfPLYBknExQ77kL/zc1+vBtTfVyN9\nsRlS0ztj7cSWa3yDPGKVlZ00MRRX5KcyaYsoMuydfRz6nGBa8UHUDb6tmOu0\n/jogkQp1srtVVkh8csY+SL7dhv5uX2OwTJo0zgxX+F7W/hewD9xNQOJaKT0r\nxqHyTMr8DDvZrAAFAOZvKlApAP5v+dDFPnC1WSyKiiXQLGqY2reNUSAZ9h2Y\nPo42U+dpKTvQv0FJtF8SsGHXVPNHglCd9YUyfHPqu3Lk/HXktr/7FiKho/2A\nlWQXBTGYHsrf1dWJF1lCEG8hStatUCMI9m3wBn3CiWVTWcjxZRiVsFTAwwrU\nmFi1YnoYHqPm4YeLnDb6+vXZDoCgvTIi1YD474i5JqJwhSNKVLwN1U/FURSP\nYC9S\r\n=WtJs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYrdijX3h+JJRpmxsGCDvZHop4e04G6rueseO+R5u3QwIgNth/+4BWw5RvBuaVy8mIvINgnBW76zClsr2Zuu1npMo="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.9.1--canary.172.fc0512a11e792921fe1a33d0002fa81f85dcb36f.0_1628507123165_0.6112085542084049"},"_hasShrinkwrap":false},"3.10.0--canary.168.b10cee3c4ba2b81251653f0dd3a49fce7ef6f6ad.0":{"name":"@sberdevices/assistant-client","version":"3.10.0--canary.168.b10cee3c4ba2b81251653f0dd3a49fce7ef6f6ad.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b10cee3c4ba2b81251653f0dd3a49fce7ef6f6ad","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.10.0--canary.168.b10cee3c4ba2b81251653f0dd3a49fce7ef6f6ad.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-21MoSWTfAArU5AyRKajEm6JKWrMSqNl9GhkYMnfqPxNpmgAoYBG7dvmXAo5ejJI4o1i2eNBWVEbA+jff2gc17w==","shasum":"f482c641be8fca2781f2774825f183c1bf05ded8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.10.0--canary.168.b10cee3c4ba2b81251653f0dd3a49fce7ef6f6ad.0.tgz","fileCount":74,"unpackedSize":1616710,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhESuICRA9TVsSAnZWagAACCoP/2YoCd4aup7WEoEe7c5x\na7UEsPm9YGJlaa6i20TEj6zgZKyT4fLFdTOS7y7HB0AotXrl+pdsKSAohvbi\nk2I4DPUunXszM3nMxvRs87vzTsHMAOaRs1aSvkx2L8xRgDEQ/tNSGexFXyzH\nwFWHnvd4te+KGLmY1nQ+ohfbZHnD4wi1JAC0bBlUV7jlLXEPgSUnwtRVOFRZ\nW5xkgQGXMVvz6AS3JY1gwKUYSWxVQ2ejgEpZyc58Y5uYAuYrT1az89m1/hGX\nZaqyWkX4XECHffYugUkRDdACue8UAV47r6mT+5cZPL11uMYeX+nwTY/fd9uE\n65CCVOTDxcGMGYEOPoue+mX3+iMXqxXzxz0BS4aYh4X9Ju/3Lp5OR77i7OrZ\n2tkLibXOf45l7ZnU3wFRbhQU2aIKMo62WXNB3q1k/oDmJbEgpKpSK15Mi9Rs\nOvFQb0gLbodsGdRCA0qfk/5Cm6n/aW+7gE7NiALYDOcu8ht6pU3ijLA0jERK\n17e+iq7KQmlJrseXocFe3LEWOB8nRPx3CUuxtZm3NGXuFUeeLzadH8OAvLXu\nLheLJiOqGKGpATx78T1RQlEd/FRg+429Q0hw+jja7EaGsRo9hm988gU2USgR\nt1GDqsg87/chm94I8efhdekDJytqGoeQwkjknVbk3kJbfZaSM1mcGZxBWGkO\nKRIa\r\n=O7TK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID30/U/2rhcNMVXPjN4Nh9onoGEkhF4MOwOg7Axota4PAiEAnFBPX5fAWsf12Keqq8RmFQXVt+pomoX4uN1GxDnfNEo="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.10.0--canary.168.b10cee3c4ba2b81251653f0dd3a49fce7ef6f6ad.0_1628515208589_0.4365552774899766"},"_hasShrinkwrap":false},"3.10.0--canary.168.91626e9ecf78e6ae7e2e15ea8032e23ce99abb7d.0":{"name":"@sberdevices/assistant-client","version":"3.10.0--canary.168.91626e9ecf78e6ae7e2e15ea8032e23ce99abb7d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"91626e9ecf78e6ae7e2e15ea8032e23ce99abb7d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.10.0--canary.168.91626e9ecf78e6ae7e2e15ea8032e23ce99abb7d.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-jcJ/9yKxzvmtDP9PoIjVE6E8lNz7eBC85pYRlbkN9fXzbPtATXyHgP5oj4GEtLnWEHpfk+Loxcb+A7JeLrghJw==","shasum":"4cfc21acab37135b5803989c2ca451f871e02c41","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.10.0--canary.168.91626e9ecf78e6ae7e2e15ea8032e23ce99abb7d.0.tgz","fileCount":74,"unpackedSize":1618600,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhEjAACRA9TVsSAnZWagAA9FoP/3KzH3oM3eUXeUu22eGb\n4ZBaAV/ZBcoVvzOa2DoqwprcwKlvfMXlLNWCRrIezyz85+MvXnXmulT2k1qI\nePgiDRTcsSeI3wxnLfu381Krc7miZOVYlqcHGXUTeEsDhrweWgdKymAz4gUI\nmK0bdEtWC6z2jxups9MRszl8Up4HdFgcYhHdHUDFv+cValk/CXmBw/HwOfUO\nXp6Pyw2BvOAaiEkkb+vG2j/Uw1BDgnXkTRj7YXnOeFxHc3CZgt0oxuy/RWE4\n2tHMDDoS4+q1FeCr5SmhYcQIyc4xaULQPsN3ItKCDCpron5jNi2H1Cx29w+C\nfbId2Joegxstz4B1nUQIeHz0pXxp9Bg12ZYCmjRbStFRV8Q2PmHRDOz0jimp\n+Sb7pxOb5Va0zWa5dAL1rxdSkhqlx0se+0r8RtOMIdypTxncZMIH/+rlsqDr\nIzSD4+pXbUO8H1ARFkv//ISdTCpYlZ61ZmhdX3wcOILxwVANFM7L+0J5bx0B\neTS1Q8O0BXltd1XUGvrcBGnfkXesYDFfRddZqKTfybFYIFRBT/5mr9S6xz0F\ntGozE0YXkv3daqaxccJu9OLpVPu2+6M+QrBL3SwH3grJKRkOQeLBI6cYrQF4\nKCEEQ0CEN2rTpqkupr+bvg4uKCx/e8N97TesH6ZqoHR0Y0CBuocdnB5/Cret\no9HP\r\n=SkT7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEjy5NGPZ1PyT9nmhGrfyLxdwLZxCbCBHxe8Vpmk6PGLAiEAgxT89vQ7vzI1HGu8ex0VK0qfzzkljaCMYW7nqXV8x8I="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.10.0--canary.168.91626e9ecf78e6ae7e2e15ea8032e23ce99abb7d.0_1628581888123_0.09401010235105356"},"_hasShrinkwrap":false},"3.9.1--canary.168.0e45c6c949e6557c436430f5ad2e26b418ebaf9d.0":{"name":"@sberdevices/assistant-client","version":"3.9.1--canary.168.0e45c6c949e6557c436430f5ad2e26b418ebaf9d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0e45c6c949e6557c436430f5ad2e26b418ebaf9d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@3.9.1--canary.168.0e45c6c949e6557c436430f5ad2e26b418ebaf9d.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-OvxAG2Oo4IreI2XkRSl9clXiat3uVV9R7YYq0xPMcQs70Ih4aLWBXaD8SPU3EZ/TtY6/0Rt5WRSggE1yzBqvdA==","shasum":"552289a140c759c93af4955dac23c5b7b85478ad","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-3.9.1--canary.168.0e45c6c949e6557c436430f5ad2e26b418ebaf9d.0.tgz","fileCount":74,"unpackedSize":1618695,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhEjguCRA9TVsSAnZWagAA78gQAJ82TWjPppLaKk8ru+7u\nVfj4TRHEgzMGpJ6Pgz7e959je2/3isHW7YxpI8F68AuSxRiFF3aepoRK3CIO\nMY4lcJcodEUBFtrcB4kdnbgSzs/zISB/2pEECd+Rz7HW2qHSxDK+nHwIr9ZT\nrwQqO2qaStN/uT3udVIYV2WF5vDiVK17+aKgKDNcraIHA3dWPN5EJp1WbdzI\nQUCiE2TH5TRTOKpyqaxba5NDoaiTcYhtBc3FcHl4nWXwl3bB3x/EloFCXR26\n1hGVxr31wQtehReJ+nT8ZRvNVRGOgfDKvJXwBeIaj7wbpJjGABH1UfkNof7z\nHafn804roVeI8RYVOUNxkQFa2Ww1TFrBjMwQKLxhRvZQSe3GoPsFPEDd+Gl9\ngEfn9C1jw+R0KBkWCABeUBL18Dc1srK0cHYeZCLxbs0FAwW4ilz6CwBQnQCt\nf/kXmQJxomMPgx32o3+SuNxjh3Xh5En0WMZO5QmqI2Sv8hBFcLPlIjxgE2hN\nJ++YaADVmeEeSzQcu/2wStNpN/h9mQsbtM2KA3PrpqJUtRT6pf4KGyysnrJe\nEJ6SRfNbsgTozKpf8G8PzmtayYDqdlV31EHmQ3JEqJ7npjVtPyMCze8qlwWi\nt0xppmecBuLfqot1wiHc6DQmpP8p7UsVYaX1HhhsoTOVXvbhxsbkwhi+LPet\nsw2g\r\n=ShRe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID1Fguq7/Xl/IqAeEeg7TY66MxbYv2oi5k2BBoTSdlfaAiEArrTDpfKjrBt8Jb+QVVerRxaWkB54Ua5wmSKDjkMW4M8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_3.9.1--canary.168.0e45c6c949e6557c436430f5ad2e26b418ebaf9d.0_1628583982726_0.29782818866017835"},"_hasShrinkwrap":false},"4.0.0--canary.168.1a348e8a90e19edd9fd8ee7d223e8f65ef9b7d6c.0":{"name":"@sberdevices/assistant-client","version":"4.0.0--canary.168.1a348e8a90e19edd9fd8ee7d223e8f65ef9b7d6c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1a348e8a90e19edd9fd8ee7d223e8f65ef9b7d6c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.0--canary.168.1a348e8a90e19edd9fd8ee7d223e8f65ef9b7d6c.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-FljALHNTr2+COfiIoFjghA3+QXG8zo+SpfEs+7aHwe+CvHqcF/1zB8v7wAOMV6wjAeBJM8mCtnwN8PsGmO7XFQ==","shasum":"5214b36345a6664bba4bed120542e1468f86db62","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.0--canary.168.1a348e8a90e19edd9fd8ee7d223e8f65ef9b7d6c.0.tgz","fileCount":74,"unpackedSize":1618695,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhEjlaCRA9TVsSAnZWagAAaNEP/RHkEJsavPciqGIA/PFZ\nH4XrUuU7m5UXevKncAJAHlYox9KiAnQoQVi4qUbmKFccKa5EIdhgB5Dv3e3M\nEnUfGfpAAHOZPE28XxUHRMEq/9J/UWVS3qZKRedRZUWvXCHsCXYuOQx+FHbi\nBy3L989vfkaAC6dmi1oX432d58hpR3GIU8+VkU2hIYdOq5slcWkUTsNV2kSd\nUGvBusRWNxhNDYJhvO9SSoB14QQxjQJ8OejJdBnTWxoryEtiwZmRchjn8USf\nHqSfbuRHQ1Fb7NJxPGvNCQKsUpVFeFBwCKN3QGDI6/q373dTchUQzTSXL6AQ\neZABAnzTwnwvyCo0YhMzUYbFIAufPMpoqS9o1PGGkfj3ks5L9UppATlIT7KH\nn7SC45b2bYT3jsB9htVCiaXNH1H0OxjqBYvq9XOww8IbLpvEJdZ5n0KnsN//\nheOI6wy6MIfCbdlkinRXuWsrGBNjwt6z9MFQejLjN4I8IsWLrMGLWrIT+MxM\nRArJNj5my+mII1UTo9kP5l6WwEGbRINGV/hKygk/EmKW9PH8Gphf/mGSM7y9\nMM9jHrXm6O0Tdo6PqjAfXoADxMGlqYicSJw/9BOy5Fse0zQWtwmMHhGXm6oI\n7z+m+e1YtcqDjezjtcVzR8b+IYa++r7R8S20KXjaNV7b8UpT6OHYwEciqEMg\nXd5r\r\n=f3Wl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDkIlYCeqSNgsdZv9wFVToV/8LTLFDS1pKxjevIt/04awIhANzmUqjGzU7H+lX+yVixWRueN8oVXmPSmqBXAQ4e9KCc"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.0--canary.168.1a348e8a90e19edd9fd8ee7d223e8f65ef9b7d6c.0_1628584281934_0.09643975195043342"},"_hasShrinkwrap":false},"4.0.0--canary.168.99d7671e8dfaa4602d1708c2a320a3b40cbf90d2.0":{"name":"@sberdevices/assistant-client","version":"4.0.0--canary.168.99d7671e8dfaa4602d1708c2a320a3b40cbf90d2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"99d7671e8dfaa4602d1708c2a320a3b40cbf90d2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.0--canary.168.99d7671e8dfaa4602d1708c2a320a3b40cbf90d2.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-yY5XjSE7Lg1oz3Ew3LDophwnziGamyhWbAmNTj9npqg7ouJxjb2sPeLVJQdyy0rgeT90znAlszJNDDncDQHVRA==","shasum":"45753330ebb4ae3b6f9458488ff12bc8b28a4be2","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.0--canary.168.99d7671e8dfaa4602d1708c2a320a3b40cbf90d2.0.tgz","fileCount":74,"unpackedSize":1618603,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhEkLBCRA9TVsSAnZWagAAqHAP/iXp20l48znvIWPGeHFl\n+gt1l8+fyaUJ7zzDVK37amydlr6xVVkb1AoVXpIWO4odqKBwqwneBoKMCi0+\nzQfkE3LsQ8hupuKxeE+Fee57LoRHUITUivxyrHTnSAKWaENUVy+llud5/T8F\nEjvoxqFRiu0JzvZoNAiX1Lb8swlpT7LdeMw9peoEWayj4SVP6eMQLhM2lVdu\nVl6GnHPZHSiXiqno6ce1Q5K+fKHHeX8J6U/daaQ3b+uV1LB4VQKYOtmd0TNV\nnAKiqmwj+Iji3bomGnz6IXavFbDXqC0J+1s0qNfISS3YY/6Tupbb//ld52a3\nwdM5JicGBNSv0DHq/mpEgioyO4yZbGfxaMr3QclTfppYk14E/c4k/m8bV5xL\nMg8yV1Ibf3QbUmaBuLSOZrnOhke91K7u8uf2WIoTpoSrxtff+uUtoaqPyUnu\najPWAwA0YLVFwyBbal00RjIqZ3UuZFo0EoifRypIzl5rHVqEkGke9eV6qBce\nRSDhV3ZPvkgu+YO/gV6ucRFQ9O5uUn/MAH4NT8Xq8H2eIQUyhWMpAzw9r3LA\nr8LNCG/AhV2oM06WMnW7xshimiZArxqkutAt2Id5atBRqeGISma/50CrvH+B\nBo7L2gqqLYUciwvtpCpb3QB6I0d0iymsOmQvwcabDYE4WaGOVJiBPVFTlxZR\nreDz\r\n=pcjR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIESSqpHryLO9tbP3YAuRC1h0zG1ohHeiaCru1mowqQ8AAiAvlv6C10e/bBpk7vzZMQXsMveikBnVVsgw3kfnQZ+Bgw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.0--canary.168.99d7671e8dfaa4602d1708c2a320a3b40cbf90d2.0_1628586688780_0.2909732139183989"},"_hasShrinkwrap":false},"4.0.0":{"name":"@sberdevices/assistant-client","version":"4.0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ffbea5aff337e168df9f33111eaa86e8306e0486","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-rRn0881IxAt5WmMw5vxRjN0wReZa9ydXD4TokV6z8EsxIb2O4kPlaXAMRjU1cjTsQE43/6rW2fOOzUy7eClbfQ==","shasum":"775a12280a7c471ebbd3c8d4caa4effa256d764c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.0.tgz","fileCount":74,"unpackedSize":1618716,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhEkSeCRA9TVsSAnZWagAAIoYP/AugFBIPU48Jq+7ZY8DF\nEgCoAAWaGjp2kwhqHh0eC3aNnu/hSbWtpnIsywcFKUl+R2OG6S3ZE62MmuV5\n8GoPd0sIMhAUZZCowhi3Zfgbh4Yy13EjiFZ4ZCPzlQudLpcvpkMlHzagIFxt\nE7QhUHH4MoSpp3b+MGAJO0DEs/LmZyvyw63VrcYzLbHIVs8R2qZwIemXGCdT\nAMC3azxlN2Qs86qHUbWvx0xgZzK1qn4Hou2AJHcNU5rQkH7wBjWgWeh+gJZe\nSmDFeuCe4pJhT74rmu48651+uhoDZwzL5V1+VP7HppeAmgR2QopdhSH7/IHa\n1xW2DtbOZuhq6obQTqJGQMNvg8+mu6L1aXh2gYxU4G0poHpv3XoPQ249srA5\nmeBS0qVDV7oTKrvdavePcuyPJNM12HWfWCgxxx+UGsiFXXchLtR3r10fLf4B\nY0EYx/0hFu7bnD64gs2/3S1kJ+WhebshAUqcikkyAwKk73FiRcnJZZS+Q4fB\nGhXHh72Wo2beF5ffFudCOwCo9uqBXE1N7XQz06oNL8BqBNUnjKivOjjudHM+\n3hTVoZFf3QlJbpQQz7wal7v4IQ0a998FTnJ+GfIT/q0yOZ3g8HDy8MPW95UJ\n7f2f9O4RChiIz9oXLedWSJvpjKWQWXJFj37lTywoEoTjRp6MyPQUJLRYz2Vc\n1S61\r\n=eWgD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD58LFnjE0MvYy82P3cyxvl0xjT8S3qZEusoIvwAQZ5tgIhAP4JkBJ4SiQTq/r2LT16zyVexs3cvO/aP5en4g5mvkQj"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.0_1628587166060_0.4836475820854378"},"_hasShrinkwrap":false},"4.0.1--canary.172.839903d45b274252648f0479a4c494e3a3e5d83f.0":{"name":"@sberdevices/assistant-client","version":"4.0.1--canary.172.839903d45b274252648f0479a4c494e3a3e5d83f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"839903d45b274252648f0479a4c494e3a3e5d83f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.1--canary.172.839903d45b274252648f0479a4c494e3a3e5d83f.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-X0BspPiWbCk57Xyd4n+lM+7Luv6ypU0YLYRwELxmkTd4FLy5bzfuijgn1qbQbb7WTEyHZGCRCreZfaAwk4XWfA==","shasum":"e648fa7fadb43c8bf4dab7d159be3dd9b4768acf","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.1--canary.172.839903d45b274252648f0479a4c494e3a3e5d83f.0.tgz","fileCount":74,"unpackedSize":1618622,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhE3/rCRA9TVsSAnZWagAAxCMP/3/2t5GG1vYVFh2sge5v\nwJVRJhVITuXKlcp3ymLUwjfWANSiYxyz8emIaRUOhG4PCKeVUjA2lpb0dRw8\nx9AVfSt+vwCx/kNswDBTwuKFsiz25vYASY1bn8rYhgHw/hk1LWTr89Rwdyai\net0WrMyvjfoebaGtlDeb5NhwCj+yHw11T0xBTrZr3IinFNgDWeBtHtD168dx\nl/EGSyjP+xzF+/1rQ8cuUAulPK+MzshBREQfAoMhGCT85FzP7nB2KhhTbxSJ\nHMO4qvoiJgH1x/MJdbPYSQHx4geGQ3Xn8usNskrhzAd3DdLUsFqQEvjetkro\nKywPcmQoF3aWqtGeeORx7M29h6KH7fwEMI8yOt4oEdfz7IArffZoA+C7wiOD\nvUv98R1GTfeQCSoibS3jZXrPQmKPcZfXT3n6kuViMZgyvtfS0VKOxIP2qgIx\nomr6V5PxX7JSjda9vDGgBI4kpVfnfXkW0qanqUW5CsujUcpiH8FDkVMcZm2O\n2YpnuHn6BpoP8rfQLIizSug/ZFUheQhpYsi2e2Cv12AVtYbakJ+RKHmhUqaR\nRq1kYLQU/KLmRZexLOI8I0MIbJIDE85PJOyC+uI3he1ZjcN0qjWDBBL3o+PJ\nMgAg9ELHzMpCy4O8Epyjs7pLnI1mG6VU5wCoEv6zHThf2piK56vQMwIrWkni\n30G+\r\n=d6nn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB6YBATTINJqX/ARxRwUCjlZMCzKFp3WS+qLIglf2cp5AiAdWbAL5YnhpI7QFAJdy0qc0xNvRsnAPC6dQSZBky/1ZQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.1--canary.172.839903d45b274252648f0479a4c494e3a3e5d83f.0_1628667883654_0.1492447568322115"},"_hasShrinkwrap":false},"4.0.1":{"name":"@sberdevices/assistant-client","version":"4.0.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"49d36601a1ae946bda3c2c04ebcaf5dd66fdf049","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.1","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-xhINI4PkkacnzaU2freu0Z0pZGVGwZPGnEpqrU7u3OjIBlOEWNhKJa02hcooZ9VfljYD96PTPLKS8MqYYaOXFw==","shasum":"dea355e819ae42342fdb33108d5be40871897aab","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.1.tgz","fileCount":74,"unpackedSize":1618721,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhFNwvCRA9TVsSAnZWagAAtBAP/36NIHTAHDWLsugLVWYi\nABDm+PbLkkmdwa38hSto6mH5neRYjaXABXOy37G7yqBQLJFhS9awZQ4poKYS\nzFLWLko8kyXjNvJDc0AQT8RkYsoEIFtG40uTCd46vU/mFnrPUCM7ysMESfNM\nmTKKaJNvYyJ4UnxP14tKq+jitJEQbBbQt1YhtKpnZxk+kpowJpiXh+wAGka6\n86ffpRJ3OlqCkhG+A3aoTsmmTk0YgTiS21nUP9zsNh4n/aAhdNazkWBtU64t\n1oMk+Umm0P6rMrX9gjtSWTTx4X6x0XD16fs928LhcQPY2yax/V7FYf6y1vbV\na1qI4m/xm0jAuYj7i3cJn7KXxga5LTSMGyhfgPZAGFtpxHQqCs8eR2+lUICj\ngGPotqQGkDD2iZ3Wel5mliyVYx8DW1h4jGhmFFUZYavxMTNSlr5krrmLLd7p\nBATreyITzCS1yLnnd/PaG4XEAM3LMGgetKhE9snIXdpQ05oUAc8/tYwdV8j+\nOAhMf2GsmAvmycsl0uGF9lF9+4zk5YxdHvfKZ+znToDASrYs+IegpCnmslzt\n7X9P3vwe4uRDT1DitaUe5BGQr0k6weE2tiFZf42orKbWGNO/I4XxyLRbdigg\nWdlABHslH/fL2tbVxbT93mTzvBDX9w+N90kvctDKjr/GOkRhorz5rIlxF/xg\nAYt6\r\n=TcWO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCYC7lQXr/au7NJvR4epf5fzKkkGO1UGLWC0SL1CvD8gIgQTCH66RibnCmksPNFBYnRVCUElu86Kc6RpbEH2SNyaQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.1_1628757039073_0.3709351968481336"},"_hasShrinkwrap":false},"4.1.0--canary.173.2ea6edfe4e3d2e8c67ae4101df808ab4b1b23bb5.0":{"name":"@sberdevices/assistant-client","version":"4.1.0--canary.173.2ea6edfe4e3d2e8c67ae4101df808ab4b1b23bb5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2ea6edfe4e3d2e8c67ae4101df808ab4b1b23bb5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.1.0--canary.173.2ea6edfe4e3d2e8c67ae4101df808ab4b1b23bb5.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-7NXe9QvJAPJ9raecCcukPwbF++Qn2gseJSbgmTKp+EK5tCGPgiYdw2rVVBM0a41zXcrBs4nxx4+kIVJJ1hHxaw==","shasum":"f016624ea83eae25aa56ef8542b57c992e2ca4d1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.1.0--canary.173.2ea6edfe4e3d2e8c67ae4101df808ab4b1b23bb5.0.tgz","fileCount":81,"unpackedSize":1752923,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhGom+CRA9TVsSAnZWagAAUyIP/3PFDvlzT4YYHWNk7AD3\nki4YbosG/U9b7aR2zXTcHmEddQ0hSzpTGOXnKnQFkYUUJzYYe15LPso4Q87F\niD4v3cd3brTZuQqXBE1S/tSoU/o9q/4PZikWbcO9jdIBIhyX91ztIbOQxWGQ\nLfd3Jkoo39oViOXpWDy3pnbqcxGZbNKHPT2MBifJxgJcYeFdhzbxp+3pGLxI\nELn/Bw0cZjDhqZXkinGBm8MZNj7zgjzDDNaMnasF0NqpTyb4DtgdxSpDlfZq\nNQqWDhCEurtXyRRjfiTPWVtPafdOdKrDwVJbbWMvSaRH3v+NNhhiicjBvvla\noIaRgwKZkAtIMxDxVeESRIgrjdN/0O7I4qDfh2rO/JlRqHBA/wqD0V5subUm\nJ/skGyHFTAV7N8gaPgQ9ZbJAZ4rHvqbtBfS/wGUwAv/btfbe4Mc25xJDAIkT\nH0dv2lAMRcgO+/dK6TKxIRIU1AwH9gdcCHY2SWcRHtJalZdRd77ui49KvgG2\nAIqEe/6hup6eFbmwhIO9u9QflJdeaKsORxZP9NflFVlQVPVH02P/jUYsjdnR\nM4KvbBT7iZa2bupCpMMvnLkwi3ejpsm/oDIwcKrbDgSP7LHnjtCpnFXOQ7WM\ntUuZyCMvffSLhrIzkvFk3gdt44Jnmytw0BsFQi8/m+PBLzvQuVeqJ3I2FLYQ\n2P4I\r\n=gWFh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCCw5RsjYcr5lqbA/cX+2sqmUNPbPkS6sx/mXoQ+i6AewIhAP60C6gLs9dGsfsNhKa1vQ4CV7sHshSU3H5eSWQ50c8/"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.1.0--canary.173.2ea6edfe4e3d2e8c67ae4101df808ab4b1b23bb5.0_1629129150740_0.4773945126871122"},"_hasShrinkwrap":false},"4.0.2--canary.175.ea34ac813a11a2362c5f826d20ebfca166e96ca9.0":{"name":"@sberdevices/assistant-client","version":"4.0.2--canary.175.ea34ac813a11a2362c5f826d20ebfca166e96ca9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ea34ac813a11a2362c5f826d20ebfca166e96ca9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.2--canary.175.ea34ac813a11a2362c5f826d20ebfca166e96ca9.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-Dx/j04Od2A53eJId4EWYKm89EYLqjyoomtNaBfAhLXTE9713ywQ9eW/arfqLvqBGGRqJHugu4TQ4GNOnYU3bGg==","shasum":"1044da9aa394772c8d86e2d3af8640b20e778b98","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.2--canary.175.ea34ac813a11a2362c5f826d20ebfca166e96ca9.0.tgz","fileCount":74,"unpackedSize":1618574,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhG9vSCRA9TVsSAnZWagAAtJMP/j5dK9WOrzEQsr7SVSDV\nUnHiBmY/0MgL3GZa9XleNvbLoyyxYBU9D/QYcFsiFOJj0V3YlQVm++d7Vp6K\nHoZoLkM57vdRUQZNPHLw6EdKyeRwN1Jni+Wf4z5cycnldATF+Cooo9HK5I4k\nmouHf4vUAsHPjPVkPRhb5qAGpPynMhOtkgScE8x3qqEJ1DiiO+S4Hwm0YPsy\nOsaY0btfOeFTbXVrGYLcEYV1QzRuGIs8RVPhSFy65sioItgMt9LXyQuZajq8\ndeKxi/dIp48QNdoYJcCeNnkWtXFLfKt3vHQ//0t7kkoI2j6CjJKy2KeS09Zt\nP1BXPoT0z0boyYEH5nzi2e0yIC/YyR2t3KkeHqOVZDJ9lqMjjL/+QZz1o2SS\naqznlSLykaIjN4vWi91LPfPhtjUBE5zUsLDxZjacLs3JZow3r5O7a2Rs9gIs\nuQxDAzN4TCjGn6B3Fy6WOW8sDr7MtHaDOJED0q5JOytbwk35AgU9Xs5lSxqS\nNPMGB6gF70YqO228QQebIsHXq0qikKM6MU3qftudduzcjoCXOBHWW1nrBIw/\nwIEytH5tmcAwr4mQF44S22GWLz0/63sCOc9kkoA91uLBJ2A/L9hEXTU4nXDu\n+akC307hj4KuCIEdOn3SjqWmJoiurBg+gOrT7cD8jg9Ai6ewLFIQp/puPMyr\nMQqo\r\n=RBsP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBchX33J1unZShtxX9Yf+tfXz0YaksTrK9bpb3LNBi4SAiEAvzmISZWQkqMdkzm/oM974JEPI5OgD0hsM5+Z+JmmBKY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.2--canary.175.ea34ac813a11a2362c5f826d20ebfca166e96ca9.0_1629215698777_0.48675497240458987"},"_hasShrinkwrap":false},"4.0.2":{"name":"@sberdevices/assistant-client","version":"4.0.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4f54a859664e6b4e54a96d4a29166a7f33687817","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.2","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-D9fTW/jR6vtBKZME5idTJrsiQXesqTJxbeuU/afb2PCyYF89doxj8yoSDEeXwFxtwaBisCoc26kKen+uyrzFKQ==","shasum":"490ffc6faee22af9b1add5de2e2e1ea7e9431fec","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.2.tgz","fileCount":74,"unpackedSize":1618645,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHMfOCRA9TVsSAnZWagAAsccP/1cnHYZZTvMC+dH+JNGz\nSsM17f0DeykIt9MPW/rEK6NMOKrPQ2fqJqiZtJDC+0BUoh3BmVqEqLljxKzn\n4JpIcHxeqAkEOVffjWOePX6SWzT8KxVbAJ3U3Ow1BuQ1D3CxLlcNA2FTNYls\nTkw5pvULkFFxisRM0Wcn44i+qdx7dj66icy6oLKrRl5JwH6x9YDbxVy2eUiw\nVnYWL5H9pLXseC9Gjk9LTicubry9wQqH4/1F01Jq5rj8aB5HPHLJCe6BJOcY\nQ5We+2gsp9Bx06PetZFmyHBcavYd9rhVG6lyyWimjeA30OZ3iz2ASgb+YVBk\nqqk71iyqZq6fOW3yds92+JA+VnTneQDVXf/SV0/qdDrkpu8iBXd8j+L2wR4W\n9xuUrUYLIk6IOX/hTlZutVuFVjYxo/XXExJ7rd9u6FB7I6jYzK41SQowiyqs\nOndx3L+z5xNMH2HPHi7N1160bE2jB9etzFHQHpXC82GVD1y3PKyDcIJXTfLH\nIHkWOK5o4ReRo7iY1WBdbhVkpqYcT0rK35VcxQ6kgDn/3LxLmp6PddPFVFlm\nGsBggoZq7qoYgPPAGMp0oCz5613ovQWvO7kQ07JCJdjr4DVRAs3YKT+R0EXy\n4iwOyg0MKQs22EJnehuZAtttWcdbPVFU7I2GYG9R3ripH5bQRMTY21/q8REi\n5V2j\r\n=lA0i\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCoEWjN5G9fPUjc1YCDffEkB0qt6rU9TC5xR5tm3FJOOAIhAIBSuB52GiBVR68D2orpEyimkuDQ/HjJDZl+gudxvHJN"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.2_1629276110253_0.09930773559822526"},"_hasShrinkwrap":false},"4.0.3--canary.176.d5a7c3b828c3c7ba08d2e9913bb51a237c167944.0":{"name":"@sberdevices/assistant-client","version":"4.0.3--canary.176.d5a7c3b828c3c7ba08d2e9913bb51a237c167944.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d5a7c3b828c3c7ba08d2e9913bb51a237c167944","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.3--canary.176.d5a7c3b828c3c7ba08d2e9913bb51a237c167944.0","_nodeVersion":"12.22.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-Qz3M515NBKwNSSfVbDwNf8gQAOy7auep8Tt6UlQMQdpLaDRO6D1TO6u1k/j2urUAZi9hF+vjPUpicg352K1fOA==","shasum":"f9fe151f05bbca8ff7cfaef2c5e10cdc1403dc32","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.3--canary.176.d5a7c3b828c3c7ba08d2e9913bb51a237c167944.0.tgz","fileCount":74,"unpackedSize":1619448,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHTKCCRA9TVsSAnZWagAArfkQAJolTHcWS47girXBQgjQ\nIEUnbWUkF34DCGvtvhNBd7fErsCgCx53RIw5ThpVGMkfLIJqRW13PWkHnytW\noLXOMounzTU4Phj4Z24UHxemhoI2n1eWdNGVHPhipR6NdrPxKkhX2MbVjGee\nbso6HfSqrDcFSvKdZhRByq5LBCya5N/HitSqsXQjepXoI8KnQUl3FDnQKIW9\nPZJmD4LERN/THUJPXgPRmW/ZCOACGLBhLSr4mlH30kWdOryWBq4+oPLXs9t1\nT8mCWd5RAdRaUXoeDGDmuG4G0EAoH/96nbxxZl387N9Wwe22CxIn8d14NE6H\n20GE4vKjw6CfncbNFATp9Y8WKJqhOejH+g1fJAaQtVuNNRx8QXsBAU4zQBnZ\ngS87M6ACn6zRRXCfoOsOJfCkQOlTlLMcnmsMpE/W2D+/DMzR0BU8r17XSFuj\nClTP7Krf5fY3IeIIz9sED8jRf8Az+jytfLugPZz3Yk1UqUBBazdj0Acng5fO\nSooZPY0an+TQzUCQUHYmUHqjG7/ClwbmZG22q1cbdkE9BjYgc2soi5KKW2FX\nLxywvFPXxjFoLCLzURB+Q68dHACl+yWmCUFV1O3mJKccYzj4nGGcyJXFV6tB\nNZNB5uzjlHxNC4OsY4RvLlbLLNX/Q3mZyAi6ID33o3J9aTtj36V2/xF8VSbS\nc3KT\r\n=U3sq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAerDfcbf6lbqezhBUWs51KzMb1EnYwX6rUGa/cIaGcMAiEAptAWFbVPFHavZZKXUmE1E6lk91a0w9t8JtHtnLlnzfA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.3--canary.176.d5a7c3b828c3c7ba08d2e9913bb51a237c167944.0_1629303426121_0.2316660470818872"},"_hasShrinkwrap":false},"4.0.3--canary.176.89f28f85e25f83f0db50456ab6999eadf15e4d4f.0":{"name":"@sberdevices/assistant-client","version":"4.0.3--canary.176.89f28f85e25f83f0db50456ab6999eadf15e4d4f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"89f28f85e25f83f0db50456ab6999eadf15e4d4f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.3--canary.176.89f28f85e25f83f0db50456ab6999eadf15e4d4f.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-CyEXgKooqFjiLSCVE2wKIoyMN0raFBAoixwx2a/MUTz0p/GiC+xDMCHNZFGEf274pSy7iVPNRm7T/pTVIOu50A==","shasum":"3e5804ec21681d1dc780cb6651beff7a74612fac","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.3--canary.176.89f28f85e25f83f0db50456ab6999eadf15e4d4f.0.tgz","fileCount":74,"unpackedSize":1619111,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHj86CRA9TVsSAnZWagAAcAYP/0CD12XYRKI4GfxSAjH2\nmHEZWJTroS11g6PAMAvsp50AOu31TvH3m539jGT2lbcnQF4mozFMaf3cztYz\nJYKzSlmuQRfITGuep9xs+EQRbhCCmjAzCNe6dSKmsU2veX88ZmdzIgpeZB8V\njW1T8YfQ54GKaka7NpdnTFH75uI7K7Ul9yDDnCp9f1t8aFg1kCxBN/dDwzfg\niBBL20pHhp31R4nopFJ2BaSomybg3SenKExpny/mugmK/pWmOGxQFnxT+Apb\n4iKFMz42jYiaoXnFaBc0ZVnMCFjyoB3czwRySLGr0qXT5xTL1R2AD4Lp0d7v\nrpqfx9fiD4s7Lbkdjg5c/xfGpumvdw1SB6IaWtLVwl2HyA9j4E5zeSJxbpdf\nbESPGmz6O4r7LZ2CI/Yo0tQGJ+rqGJKB/1BNu45/hht0ev/Y0dRJfjNA3A5/\ngINGhxoQYvM8Swq8k6fs63G/66OSptZ5Ovm5yQQUmI9lR/rio6SyqWdQEuH3\nZy3cb1tMZfaWxZftZXhhXQ/Fy5KXD9E4iDjDpDGLel5NEY12rFiARXGlTgPH\nX5gmHYbor4hmqLxPf71U/SmfSkB9jy7v47t47AnsTfJWiak1hky/1gCjNver\nxZkMilyYKmKPHSo75QZ02dA5i6liJzizXNjDmsY9DLdR6ZUQmksd3971/5qi\n/Ok+\r\n=YYTg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDtUjBqf6Tr6DGP39a6Rz5Ew92OI4Iw0nFg5wnfyewTSAiEA4JCaALBtKBS6Rvoj7kdHC8y7HSvtkHBRbSWTsSR3cYg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.3--canary.176.89f28f85e25f83f0db50456ab6999eadf15e4d4f.0_1629372218304_0.012887026814065061"},"_hasShrinkwrap":false},"4.0.3--canary.176.035d5dcbd98c3128e2567567f23c4a60fefdac89.0":{"name":"@sberdevices/assistant-client","version":"4.0.3--canary.176.035d5dcbd98c3128e2567567f23c4a60fefdac89.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"035d5dcbd98c3128e2567567f23c4a60fefdac89","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.3--canary.176.035d5dcbd98c3128e2567567f23c4a60fefdac89.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-wcEKQ6P8fHtAP+eFCCwbfxaccpLDbUqVX07iCzUt0Y0iRF727p3btZ/27raB85iMmrp5QnPA2AuXo4oQbsLraw==","shasum":"891cd201246129ad0e0b1f604ef02f7e1fa3c216","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.3--canary.176.035d5dcbd98c3128e2567567f23c4a60fefdac89.0.tgz","fileCount":74,"unpackedSize":1619144,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHk3aCRA9TVsSAnZWagAASuYP/im1YUKSD4/lN9ynDPik\nIgaX7ATNHdDae/xnUh+WWPmwU5MjvNLpqdhd7iKHSLpNuPJBx6vFSn4uKsnW\nbHNpyHf7DhKrfxiPe9Y9zl6dB075WTd/MKMSwCzZSO8FTV68uU1QjQocY8e+\n1VYvC0TKch3QQ6wg7RQcihyNXIP9lb6IL/B196qXXWXCS2FEE9pVLM3gKFnG\nnQm2XiCLCrQDGtM8ox5SOFsJ8MANOmi9q+/QJ2Y9TOK5F9mlZ0dIRMhzTqE9\nHAJNWcunZ5OP+ZV80xjHQpNyG2cU5XO5ce+e1kpI4KOcvIQr81Mkj2xRwCDI\ni1O1sKKZOXApxtjEkNBTztwuA6fsIXJpthG/wGW77L0wa9m8ydOmI7cigWLK\nUGlJ48IlP772YADtBcpaHZMbnM8Wi3Y4o70OPFv+GNuMHQjKthWXe6O2CsIH\nUzzON4w2s9yR5jX2mTWJO/9sfRF0qyv1fnBx7VB+vQxMYrCtnlY90+2w5WzA\nccXX5HC2WObwKaeG7khrB3Ayz5PdLTS6aQXxN+m8N/Efkj0z6sPT7DNxV0o8\nYaakagN4VmiONZ+d8x4O2yOZjYTOxa0C+SjI3+5Bkrjnb9Wp+ttHw74u5+dU\n5EqxfSsxOlCnBSQJ9+eYeTcgNX2GHz/NfSeTBX0+c1MPJqp1AZ9DtpHTHwIi\n3Paj\r\n=bqL7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHVDVCmcM97q30KsvPhL1qfn1XiKnklu3n2Eoi7gj9z0AiBTIlFVFbRxjcsvPzCZBHNi1sv2aWJLsOKqTe4w2gGTeQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.3--canary.176.035d5dcbd98c3128e2567567f23c4a60fefdac89.0_1629375961791_0.2663743049214453"},"_hasShrinkwrap":false},"4.0.3--canary.176.7585c299cff99b882988bc3c2b555128d26c010d.0":{"name":"@sberdevices/assistant-client","version":"4.0.3--canary.176.7585c299cff99b882988bc3c2b555128d26c010d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7585c299cff99b882988bc3c2b555128d26c010d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.3--canary.176.7585c299cff99b882988bc3c2b555128d26c010d.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-YzG1nTWG06f4v6GXbd73va9WMBZMxM4aMnA9lmc1dIVoA1514B4EBAV2IKb9ab+wH2UVuKSHtY6vfO1agFFkwg==","shasum":"a9a8c620a24f9874c07f40f49a4814542c6659fc","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.3--canary.176.7585c299cff99b882988bc3c2b555128d26c010d.0.tgz","fileCount":74,"unpackedSize":1619141,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHk4YCRA9TVsSAnZWagAAV5QP/0sLH++aeUAd24TXJFVZ\njpxIjQst2ML8Wfib5C16iAsc9RI20g3HDwC4BKI/6HU9WSv5twM3i6VrYNY5\nYR3OC1rlXwdLKx6jw/Zb1GWxmnLkV2jhi45oZHydCPZivNGARv9Ktmd1XqaB\nxtkFIV0SiuKF+zgnM0LEoonhYcx3iceuUlNxbzVnh9U8/k+WKGH8WHGJyOPU\n10/KFMsyLgQRWLpUCAu7Ufxx0aItypRMo3j6EjRlo2Ssv2iYLsbZQ8kGuMYw\nMwLQ29bXb2l5m4BmiUVdJh+EGErdXh6ZWYSwCKYEcIJ+nXBTijp7lLXVliki\nwFRpK3Dax/5BG9Abv06VPFZD6dZ2YWMw1FZAKxHSZ7mdPFc/Da7i4Yc3tDdw\nJ0wSRyUf2jE8x2VDTjbnbg4HWJwx2la2pYQcHYPXVglxfCYquFxBGVWHmRcp\nJWK1yDjxeIFcemzuekdoz1GzPasn0M+QqqYdkUABruaeTbjQ0goXvEN2H8bv\nY8inH0Xo4PznMVkhNcsrxzpfq63It4rZqrAq/7tvUiNoTOPOZVXuFLiPsxQC\n9o994/DLneSmdUBi2FDVe5uhCeVxWHDow/yL1ph3dE9IP9I8SLTFTdozJbxH\n92+5vMG7W3cVNxZxb9bw8mT20Tfg7Dzv7813fWdxJThx8qq7iktJY2XfoffE\nymgT\r\n=Er+T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDMqubayRXu0L7EtjduoyVMk6n+sREjgG5yDGQcwdB6xAIgDDlf5T+p4H3Le4qGprn3auqGhVI9LstiFHK5vPCNqbA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.3--canary.176.7585c299cff99b882988bc3c2b555128d26c010d.0_1629376024027_0.15572984346571772"},"_hasShrinkwrap":false},"4.0.3--canary.178.b0b950cdb22f9caf09649fb730d27d218a31e603.0":{"name":"@sberdevices/assistant-client","version":"4.0.3--canary.178.b0b950cdb22f9caf09649fb730d27d218a31e603.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b0b950cdb22f9caf09649fb730d27d218a31e603","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.3--canary.178.b0b950cdb22f9caf09649fb730d27d218a31e603.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-cH0PvnTGIFC5qE+fxX7JYMojdZ6Lzy95T27cxZ/mn43j6mc6wYfXh8qOtQZYTPO6UZMiSnItjZl07G1Jhdsj5g==","shasum":"9be269fc699106bdb27cf47ec9bb7e9f95070c9d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.3--canary.178.b0b950cdb22f9caf09649fb730d27d218a31e603.0.tgz","fileCount":74,"unpackedSize":1619107,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHlNfCRA9TVsSAnZWagAAd1cP+gOw4hW+K3LxI8CYRxhp\not6+x2eRmaihW4q3tBQD736OsRqtR8MRbYCyPtCAyOyj0fixDuW/ilzRK1E+\nW2kMaAH585FuaEHFLYBA1OWrjSOeXsXw08SK8Qp5lG4fEM9sG/OIsDJP3A0W\nF3nzErM12qcdQ7n7zoDIdvwBEe7DNskZGehZobEqYp0rAo4i6ZVe/cnnTA44\nALFXRtFyl130B4btL1M/Bq8tqW+lY02u29MORW7ROpWpOk4UfiqCnQO6sMXb\nRdeOcqSfQu2GQkVq7DAcYdJW86o0zGNpCDhQLNtD+V96e8q5R6Xc4UNyOsR+\n1od/fmllckV9Y+K9CoKiHLradibq9/0jXGgllKmbtP5BIEXwhpRMY9Z8hxcu\nQlIHq/fZmlp/wOojzRfLt/TvHdD46Hj4xvzruPzZ09dVx4+rUq+07lgHi9Dt\nyd7/Q8rScatDb2NUjxilGecaMw+kGGdcWns1tfIiRLXtfpTOJe/ilRbGSWb9\nUTcqP3dKilx/7A915kQHTeAfZeWh4nF+gL/i+QtSEGuNdnRK1vm/FETPzjWR\n5ZrgxeruZ+VBRhH/C8Gtra0AhIMJBk+cQ7CBm7GNa0oPeDg4+VbKCmG9Yh/+\n2/DG1hBJoZQI++whXYXwrhB4WhuQefb9RhwHoYU7SJ4wJXRsvOXWATYwxFcq\nurD0\r\n=Awpc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCR9Uw/9F5/5AhrGPXC5sBPHlv6DQtjSfb0z2/uXd6WRAIhAIDCKb+HrDj7pVjuXzJQpaQkOOn8CICzdgO5m1lykGty"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.3--canary.178.b0b950cdb22f9caf09649fb730d27d218a31e603.0_1629377375205_0.8041851335907735"},"_hasShrinkwrap":false},"4.0.3":{"name":"@sberdevices/assistant-client","version":"4.0.3","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6798e8997e0a658fb25672e3cda2e1aae87937ef","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.3","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-TQ6CT5w8hvHS/NAXygucLwjMWlVMI7byKActvyvJx7n2CIhB0WwnvpN/t6T0mor7lfT0bOQwpQUdI+DnN6UKqw==","shasum":"06184146fb6791b1e3b69eaa630d475a4a1f971c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.3.tgz","fileCount":74,"unpackedSize":1619215,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHlPQCRA9TVsSAnZWagAAugwP/2b8c3El1gtwZUOBktm7\nGV4Yi5sgjDmHe3GC7vX1ugAroLY+YnK54tFgVPNREdvF4jLTAh6GNg+7Cg/d\nPKVxD1qX7G5fsKYfP6K2BkpH+uLPpPNVftK5Y9sDNRF8UN/WJ2zGB46DRgF0\n0EXZrnlJkwa363Qj88qTMk+Tn5Hpzl+GwyG4wlqPP2k2AlviWhCgJHzSSFas\nX9ZF9zwQuV2lUfKZCHCTgU3X0eNKHr+sRnoxrAOnw7Ka+aAskSJzsakxMKR/\nm15Gj2+1Wkz3lluWUjLp175K4QXfq624U6Fo+B1/flQt6S948ADZJYY7VbfW\nf0bDDoJvFTqJOD6+/EtRTBpprY7Uq5H++iE907ewkuvCkuVseKKETfwwZ2v7\niGjGX1qN1VWl4kYlaPgMZ4kuEs5KwPIkp+lDq1eBT2LYrNZ3WB6gI/4aFL5O\nEJlyPovZWAKLQUHPz93yrtx1Q1HN+v7YUKERhQbsyf6VrnjcgmmkmMZRk1UZ\noft0UcuWbe8IRcBTH+tp4xWAgnrdZBzX0SVVV9Aa+Bhrk3ZMh5jYn0BCRnEk\nnp9/Oa1IEuZs48Pnrr9qF3hCP2hj8UfZgfXj3SxdTuHEagz9gGqiZcCpUTPQ\n78g6ha1cwGXyQ2JZplUYpPmt3tDEvg0CIs54BA4SvoizYUO6kiI7LJ+2t1hu\nJTKT\r\n=iNa2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEPfUC8gUFY1N8mT92RgF48g/fQxjk80dC3OJY/Ii6lQAiEA77kO3YZ5Jvro/OAp8gzZ/IUqRbD0muWH91kb67oka+o="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.3_1629377487952_0.47232979893009386"},"_hasShrinkwrap":false},"4.1.0--canary.173.8b21706c5ae9a47574fee469a0d72241e0324d10.0":{"name":"@sberdevices/assistant-client","version":"4.1.0--canary.173.8b21706c5ae9a47574fee469a0d72241e0324d10.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8b21706c5ae9a47574fee469a0d72241e0324d10","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.1.0--canary.173.8b21706c5ae9a47574fee469a0d72241e0324d10.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-tRmmI7auPiRArf7Gk/q7tEgnNC71OOT+Jm46b6Po2WuZL0e7WEfLw7z+/GexRggcfmvjNo2jhfJeQmhsOTUB7Q==","shasum":"bb236fd2230017eae4aee17685d01a319684c167","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.1.0--canary.173.8b21706c5ae9a47574fee469a0d72241e0324d10.0.tgz","fileCount":81,"unpackedSize":1753180,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHlR1CRA9TVsSAnZWagAAtY0QAKDGruNosaQ83Lc8H0I+\nslCaR+GNKtu/+53pckOQrLVfbpP9rPZ+BdoBN6HtGe7OW5yGfwXF7afhtsPb\nQ8DiuZ0HLPASF0mSIUT71HrA5tGF6lmLpXb7uo3VN0hIBW2P00LDJxRuKRTt\npJ+lF2wiWd7DmbQfhjrX63Q2fvURpQJL8lKR2lVR/8sMsa+IjIflkZBEhn+M\nuA0fePUmO5Gjc3PMKcbNkThJYBet7SQTTDNN9m/7P0EWgBhCVZDPvpNtC6lp\nbrZdEkkJ0vKlH4+NlKvPZmWaX+RB3TGHHBNtZtxklP+FQC/zbNGpMYsf1j9Z\n8J0LR29F3AfloP2LEO9GgzX6TpGRdzekrx3Of8iRVSuMYHvGZYE4f4JkI7Iu\nLyIWuxB3ur1VuvImZCw4u0oHghE1isfZPYYiswc18DcoBNPAnB9uJWG1HSMj\nYW/oux3HEQKP0o+h6qGDom6ttzObIzSXKGikLJv0INeWjvzze98fx3PA3p/i\nU/kUOIwshMSZwinNyDk62fYx7kpVkNFyAA3jZNCLWHgd3Upm4HoDUlq+HwCT\n6dPwTbUygbUnjkmGYH0klt+WTyR5gsKslRHHJ6Bm0SW8w8UUHb850qysTcPk\nUWoNGp5Ovq3hTLlB4PDC0GGuaSqzGeTLdyWTmBeAcqHasL4VD1rRJgXeJfnq\n4FXw\r\n=cJuz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDiwFZeZ6KpO7zS+AMLAqRC6SzOdhTt2uQ3n9xsBtKL2wIgfMZDdhA2+RfPZSPIPs5VDRmSyhm5ug5GHAnzcPj1wLU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.1.0--canary.173.8b21706c5ae9a47574fee469a0d72241e0324d10.0_1629377653150_0.06613506708615291"},"_hasShrinkwrap":false},"4.0.4":{"name":"@sberdevices/assistant-client","version":"4.0.4","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"387dd7c8b3e4230e1fd251110d560be73cc0deb8","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.0.4","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-Rd2324OpvdHVK9YgH6wh5KcX5A7e3Y6G05ddpspIJ1cT8lIanFtQV35CNM0W2xcdd0pZPkAooN/636xEtnZSDw==","shasum":"fd37a79ae23b69a257044c1fa83179c723f913af","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.0.4.tgz","fileCount":74,"unpackedSize":1619766,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhHmIOCRA9TVsSAnZWagAAXPkP/3qM5sW71LUgN/XwF9Yd\nJA8HOszWBwHVUVQf+6d6UjrOEvzsdc8Yf2Dn1yeK5iYL16Cln0hLv2CCRDns\n4LmpwEunjlwafkIrcocNITojE6ael2FXv0GFUs4pQgyfbx2UCnEcZx9LcvQs\nSt9VPVNJ0ia6F1Q2AIRTfNd6QRL8pyZH0YJ616/SuV+Y5LIbBuK7IMRsfNwT\nbELuOFjUOthiBclf+RLTc4YXbV67Ncw/0ZDEeLa2N46ga2mFWDu6E4hVicx0\nLozw1STACWHyPXMCQagY6UsMi7z10ko27G9Ux6+LUUx/jlr82kuhJ0UsjzYr\nNvoUGZ2VP7/KFSd5vjvZ9XoEntYXugBHligrnIOVlluuOlQigvrQixkgouPq\n3s3VG8WzUjPyXKNsmrihWJ7PitJQu+sYS+SpCqLkUUYpw406CdAndpic39dL\nP4Z8oiRRTpVZ2OiFn5yoqwg2V7NKaWzOYxL6cM7WY9mEsvMMCdujhGmpFNIC\ndboRwRsyI8CTEut/BlZaG3n1e7TSYh8iFIPUoTsC114IPQ4sn/KrvMVMnayT\nsrZO8vGbRgPrI1A+Z1IuqqmBJtc0Rm0ADP41f4njradw1JvNDmxRx/hjVLT1\nxY10OYNggHW3T03dCJ/tCS84rCTeAyQEL3mnGduNK3fsqaYObo8rWpSYUxe9\nHIgB\r\n=f5it\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDhNwq8oyJ32O84v5sT1p2UwvSswpEopb4VVocpkHtvEAIhAOygfhqkAf9ijq6NvnHZOm5yDZrGemTb+1XoE4jZYoZj"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.0.4_1629381134277_0.5641225794333742"},"_hasShrinkwrap":false},"4.1.0--canary.180.edc2faa1414cc2fd516d758801856c7ac79c1ded.0":{"name":"@sberdevices/assistant-client","version":"4.1.0--canary.180.edc2faa1414cc2fd516d758801856c7ac79c1ded.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"edc2faa1414cc2fd516d758801856c7ac79c1ded","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.1.0--canary.180.edc2faa1414cc2fd516d758801856c7ac79c1ded.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-nX24r0oisvt2kVSTQ4pPkxzr9wdWDbNIlClVwZpEz/JklOVrbaTuFLdMBFg0SzjFWX/cFX2vBzDNzKJkJmXTiA==","shasum":"2f79bf4afcf7a6d3cf1c6b83345b21f4f9ddfc53","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.1.0--canary.180.edc2faa1414cc2fd516d758801856c7ac79c1ded.0.tgz","fileCount":74,"unpackedSize":1621493,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhI0kkCRA9TVsSAnZWagAAy1wP/A6w053ZE0DOSbCSM7de\nfB5uUlQC1eDpbR3Nq+XFP95aWEiYFBQxQKkoPthbW2gfcN2Yp8+wTAGd1vWw\nCa695xkDVAOnqDM3EqokKhhVk+1MZ6BPvyBQ2iq3KOa6Mhe3H7oK82dQrzH2\nWjII02FjM3khbshkAZZNc03cqDbwihSuS8RKiU5193PGZHBi5Bk1GGE+mbMM\nkFQxVQQ6p/im6cgS1TjWtddkFzG/E+09kDRahwucUMG0SCrGdZtLCCGxSkRh\nUYbpNJN+TtiDGCviRCIIITdBJ7qsUzjtHhE8DRm3j5aNhDteFyWvZvlkI5St\nOx7nUE+JZUYpraWHjnvcNojf1Pt+YP1BZ114sUFUjZApE5xd1TV6EW0r1+UB\nnNG/5vEfv0q+wzbi3F5tZoBzPzEXcpyf+Yw6RcRXYXTmh1FKKubcdCSCsyxF\nMv5kz/ibTj2M9KqlM0dfNtt0kXXMELqDu4tGgUz49j/TIEkTe98KqFEMeuTh\nSeMtpZHPjbOstNvU26tAc8SqLl24BArN0+8YN39ekVIVhC2SY8Oj+Vv/bwC1\nmRK81Uh3HBZxNs2LOovBorWzSzPKX36kaFD9rAh6TRvstfPLV6SHwJVzRZWW\nLFVG7uZJhR+bwLruFFYH4Z6402cMVe+5bIjLJSC9wVXcH96pIjlOrzflXv+r\ntTvJ\r\n=Tb34\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAEZzMqzbhxtRsKCFqxW0WusPEx5qevsGh0HWqFoZdVOAiEA/Zaj+AMQT7hpLJWikJefaScZZf03BaJK0iV8JoI4M18="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.1.0--canary.180.edc2faa1414cc2fd516d758801856c7ac79c1ded.0_1629702436247_0.8672409826929093"},"_hasShrinkwrap":false},"4.1.0--canary.180.e787923bd251044ab78df0620567ac40ab779a44.0":{"name":"@sberdevices/assistant-client","version":"4.1.0--canary.180.e787923bd251044ab78df0620567ac40ab779a44.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e787923bd251044ab78df0620567ac40ab779a44","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.1.0--canary.180.e787923bd251044ab78df0620567ac40ab779a44.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-FdQCVwVowL2wh+ADEs/yYilOnRGDtwCW4PeSZ4A47g+G1dftR3ZSjrpFu0o66PDJ5PE8JqhkZ5vO4bUDhq9oAA==","shasum":"449b18f9630397948c4d82523588f8ea8f8470c5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.1.0--canary.180.e787923bd251044ab78df0620567ac40ab779a44.0.tgz","fileCount":74,"unpackedSize":1621493,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhI1GhCRA9TVsSAnZWagAAsDQQAIuN1AGCrNtLN2gWLlA6\nRJ0xcFnGk2y+idkFfzlkZZ17pdORvoa+d83F4C3Ger8M6fp8PJJdV4kq5J4j\nzyVCQ5GMDFfFDVFDsb6/DagADaCc8K1F/OOTXSRv4TXe0QEpFYBRoJCa/UlL\n5OCsWRjNjO/T5eR0Y0dncjqAFPrfowB1AA2FQgmQUVSYEabrgj7bwzkK+wzr\nC/NQV5AeKVcZ9D93P90fSkhUZZn2UOj242WohSpr1DxkptF01qID3vkK9h5R\nwlbieXVbHHxjCPAM+MYzQ6jcFWSGD68bzx3Y6uQaTqYG7I1+y7vB9aEAZQQj\nLPeKSuMxR3nAyLFVntyorOUqnXylVLYefgrJ2aRtvlRbTBEGFKqUqoTl5EVe\nDvIDtAGFIaxL/vdMZWK8n8xGtVdkko717UyRYXaexWKsaN0HqbmkGu/M4GyL\nz5zeEKx4Dr4zo1mWHIRWcauSixFvpAgcdxbb7JEhI12I4ntqGwkWiNNafUOr\nK0cOtMVpcC47eBoGer2ykTl1PUgINENycq143KHL7+zhWtod6p0eeNARB55V\nZvxTnOZKS/+AwURmITS7g5d1uXVurxaH4HD78LMQItrrkynUxgq+zbls2ANN\nz18UNBG473UNiu+cp+EZ1ihL0PiaHfb0JPjPTK5tCqURXj47J/2TUYKgUhel\nffQC\r\n=XMZ8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB1e8TEwHEHc1syrMyycIR7Vy6CEoHXhuy3uXvEe1+rVAiAxqya5o1eB2kAHSTTihM8Ml/wvO70lQi34jtqfXEZebQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.1.0--canary.180.e787923bd251044ab78df0620567ac40ab779a44.0_1629704609416_0.0010589640846225823"},"_hasShrinkwrap":false},"4.1.0":{"name":"@sberdevices/assistant-client","version":"4.1.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"33004d10fdfa6ce023b49b5cd9e6b3db633b86cb","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.1.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-o5VEz8Za9aZqG3fPyInzgxqFVxh8Tr9d/on3zNeEQCLFXULVemq2LZHv3ppBREeKAAdbDLAh8prYG2r4UcfeOA==","shasum":"f756f5fa8c942c1ade9a9b56352f9a29f16af6b7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.1.0.tgz","fileCount":74,"unpackedSize":1621584,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhI1KsCRA9TVsSAnZWagAAr5UP/0CU5weTVQGmvTCBVT9w\nwqvoThD3dfw1CjlunDe7z7i6XelLMpTWaFGRS7jh2GF96yu3tiXV5N7Cz6It\n/Kv+SVVRCytiAQIU1z3Gza0wuPgs2ZcbCXg2M5OkdwqI2hM3oxngvHGrlvyC\nbfq+CMgE7caa7vsf5CFCyMQRMRw49Cc5hlaTS8iFJ6UbKwSrupdm8grRulJi\nACManEGNnTvVr4fDL66Er/tw+8S81pmzG+ah7ZBWllffUGE9CzoplMxRtg+C\np/CPGFoL9B9dRZAh5pd4gthP8VrHntZtmxVT40Kbq5lLt5/Qo599P5XUyGPP\niDEMjGLsTaGIbG2SKN6h3OC3mtk3rH6aimFsl5Yhs8175FfR6BzcxnVNo7op\nMzy9RLjBpsbmlk5RXsrbS0cytyp4yAbl+eVZvrhP5JMTFf01aPMpi22wMVyu\nQ56FkdRURFevcsEJimcSToHLqMoaiVc4j0ydqCmLv7httoz98XwjPUjQHWnT\nOn+3I0HaUuP0lfpDqDreTDi+2mmPgICFDwxDyTn8jia4++YorA1uc7UN83e1\nq8ancNUsNAjWFnM7xplnIWJN45u8z3JUNNZItYiMwfUkBzKcYJy0M6I0EA/A\nG2QS5p7tZ+JclIchS75k8K49eeGQgJ3XiFS6oshUbeO5I/+UU8RSE+YgVsPD\n6uQi\r\n=dNCo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC6uGn0pC5e4k3IwCrr5zeBruVNFd6+qNYutRSgm7OaawIhANbTwi5ASt5srnYOQuObxK513MmvLtg0ae2bPl7SDyu2"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.1.0_1629704876623_0.007236738109670293"},"_hasShrinkwrap":false},"4.2.0--canary.173.cbd1319fc895ce0dc1a2067cdefaa19209ef35a8.0":{"name":"@sberdevices/assistant-client","version":"4.2.0--canary.173.cbd1319fc895ce0dc1a2067cdefaa19209ef35a8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cbd1319fc895ce0dc1a2067cdefaa19209ef35a8","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.2.0--canary.173.cbd1319fc895ce0dc1a2067cdefaa19209ef35a8.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-qxmokS2jE0+ugzov14EmICxWl0KfZk8n0fIFGE9hcUlDRYF3UhEPW/O7Ypmv51MHykZeu7iFZdrCxHvJaqjvVw==","shasum":"ca725037fbfe13d6c4e4a3901b58e723c12efc87","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.2.0--canary.173.cbd1319fc895ce0dc1a2067cdefaa19209ef35a8.0.tgz","fileCount":79,"unpackedSize":1752819,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJLrkCRA9TVsSAnZWagAACtYQAIgKSt/CHDgp1uM3WBoh\nPixLyfCZISSyYHIeSOefSKtfcRYNI5jKU9KlPruc9mk7PLSz6jE/Q67lmYO0\n1rM//zEj1k6tlLtaz9/bZ2P0BesPsAE/+0p6qF39RWhGiVKumcYWRw6TsSEU\nskzCH3bQhUEvvNCzI7oJEFEKotCmwIdK3Q3FpDw3wyKT80XmoZDOSBmiJNrx\nYojAxce4fnllHCrSMa+FtSD7kzHWrxNhm++AbYbWdykzryE7erKvJoNNTZ+6\nX0QaCgIY2XaLvdgOYhHiVMaX/qK6u5MQ+z9XC5xUSR2DpMz2xBQYcRXlNjB7\nbjTC0WOh58rvb2EybJJFT5iHCdt0jrzB7S/wOs7ccL+0GhKia8pDXVeMz9KY\naa8nBhWZVHwAHmHt1hpCOczt8G9NKSdfy2gUWdMnE4+P7y7eD11dnFd+p+SA\nYbBnC1iWfXX3rJcnWbQzdC9g4LuEIVViEtUhAs7Jhy7BBLN3kY9DVIGW8rYs\n7OWRxKxxJv3lVH6ZhnIk/5xf3FhGqgtqUk2+yJVKEKAlyPWkTEb5O0c7r+ZL\nZv8+0QCEF5aDqn8TJK9cs7n21qJ+TK8i87szR2SfNDtt3tP0y1bU3k7470ED\ng4n7XFsVZ6Bkh884tT1oW31d73N3jH9SYnEB9uey4TZg5bQtzr/TzRffyPHx\nvF2V\r\n=n3GD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBz8hXqqtm7azJmrzZZA3jGH9bVoz/UqCeltDRC6gdCjAiBDXAVPc/EqrM686az1I8Zd7Z79L8yoQvhmsLhus9sbUQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.2.0--canary.173.cbd1319fc895ce0dc1a2067cdefaa19209ef35a8.0_1629797092460_0.3736982585999562"},"_hasShrinkwrap":false},"4.2.0":{"name":"@sberdevices/assistant-client","version":"4.2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8a36b060290da55eb0e431d0c0027809d3401760","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.2.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-BXajXAu8KzXfGVOCdlYIm7YWOKiOED2VKMpNebGiCwzWygvhl9txar6yuYw7lr4+IGW8EkQUA/HJgmc6itNsFg==","shasum":"ba583d9144f14a9704d0e7375964403221d6f074","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.2.0.tgz","fileCount":74,"unpackedSize":1621915,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJOwbCRA9TVsSAnZWagAAmPoP/2QqaiAlqXvLI8Ksgl2j\nuo6xpzwWVcBsdavangEC5hKIlzUdRT4EqikN8fhWvST9kmszKvbkZoGAGFRF\nG0623Ka2sK+yuGqeauXzLiZj/jlZBPm70PT3tlLmbdI6p8cjlzG6evbJb+sq\n8yZqXHbHvFchJkKh9VkQIzbM/SgDiG0OWjpKT//6R777CQ+PKl792FD/m0EG\n6KDVVXlEivIs3ymB2d0dHIaYCdRh1AFiyI4Ei5nOt6M+YWKe7jHuGGFkVnMU\nIpUEGw092hFigghaXYpawaQtDoN+CkXlbmXEWFkpFpo+H0joOgmlad2W4nj4\nwqohmM2C9kBN2+EHGUBMP09NYh0FdhX/Sz11Gez3YfK12dhDynujOfvQ5znt\nihyNwJno35+BdwYMSYYQHQVzX5taGvu8YZ/CwOI/3rElkm9tdfXkuVUBmD+k\nhiKkAvKpxFxn3wAO8D9pgcPZiCZqjRJruTeowmwm/6aKVc8WtliZQysG6sj6\n2bSZ3ZXziBSa9K+7gIPIbvmRyc0A2vJNdlFpDaM8mi65eJcsFNe+CZCXAvca\noixj/59jbBlugAJGsxagnET40sCQDK2WmLeQM3LE/t7/LoYQnQPcx5uyEUMb\n6v8cgNbKaT4lLCHjS6XWve2ZGHztSpRE/w/BNUChtl6lgVZ6wJTxWKFVbH1F\nhT8Q\r\n=wkAR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAF12SFNqi6rgKV2B+z6Hxx+Jkfy0IYTDL1qh+60c/UcAiB0mw0Jc/SdTM0PZWI8sVhnieIescS8BQDkSecIFhVO/g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.2.0_1629809690902_0.13882451206734236"},"_hasShrinkwrap":false},"4.3.0":{"name":"@sberdevices/assistant-client","version":"4.3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"bb8df535ec8efba1cb9a6a7b7933612fd6375a8a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.3.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-sC2/H8I7yTusmNo7g7hV9Kc4PC6x+JBgcc/oK7zIsQBX41ljnIVXSWIl0YlI5fFKjPYdbc85jEEa4mpPLudBTA==","shasum":"52dfc47445781160cc50f7e65dd04234d0cb2ac6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.3.0.tgz","fileCount":79,"unpackedSize":1753796,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJh82CRA9TVsSAnZWagAA1bwP/jizSTKjuOZ0nDM6CWK8\nZpFVV9WbC26lQpd1clgbTmnREBoDqmbepMm/yMREcNef5dG5uzWeH79aaXxi\nJcorA3vtdll11SsLR/O/60A0H0jIFx/LlsWsBEHDrofzc85ueDHXVgHIEPyA\nITA1vT9E4DWgxvA+kdaKxRqrhBtpJb3JruqCAnndTPpPGT7ur11dcfArTB4r\nTcBYVHhh/tuMHxdbX38ay7wNO8rkfsQgMCe2EhVX1GysYQBjNEDlJYmk5YHL\nFStztYPp47AEHYDLmYH7e96V5M8HdOsmoZNUa4+ZwVvhCz9hfE7THtae7Irp\nXC/v4zBS8qz0nHrSVQT/1xX4LgTs3bKjKrILZDlpma8BLp8bWiNdgw4+YBuA\nr5wTQdIEZrklpFMaElbv1118mm206Ejhz/vz7q2qjNAEVdXLAzfPpwKe/uM6\nEkbiCPm5zo/Xw0uoBbeOtnJwmvopzol141S04rZOLLsFGRD96pAFcOMZCvWC\nv18pyMR+cm2HwHhclA3ZkV2jN9TSDxwDD0+tY+sKAVKEL3ESWKnUlL7YHsA7\nXcEJIFcYzwMiNE90//90IEDxzHKJ4q3fFziMtHguJbrlpRvv5KT9kljusI30\nZkbei0VkTilTA1Aoh5ehSPUleSiJo4LxV26afOHGQtyIVunqLFQSDshtkiyB\nXbxk\r\n=s78E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwffNaj5TfVdxn7NVIU980tqOhpjTUqPXqNzVPi8egPQIgUQVUvYw9czLp74jlcoJQhe9qdhtMQHrlL053B1d8xdc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.3.0_1629888310178_0.9325092680793197"},"_hasShrinkwrap":false},"4.4.0--canary.182.cf66a112b36dab90ab2433d7f41b7dfec0dd4cbc.0":{"name":"@sberdevices/assistant-client","version":"4.4.0--canary.182.cf66a112b36dab90ab2433d7f41b7dfec0dd4cbc.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cf66a112b36dab90ab2433d7f41b7dfec0dd4cbc","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.4.0--canary.182.cf66a112b36dab90ab2433d7f41b7dfec0dd4cbc.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-506D1bQXxgGhRF436vLLOpBwPmW2Xnx/dOAcCmItbvLFYYk1jhq4aPilv8Ua3T3a5v3l+Z65INQt1qkP0xcQNA==","shasum":"c41baa74a6a5f3d2a2542219aab846124c3be8d7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.4.0--canary.182.cf66a112b36dab90ab2433d7f41b7dfec0dd4cbc.0.tgz","fileCount":79,"unpackedSize":1754165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJ0XWCRA9TVsSAnZWagAAsMsP/2T3NVgoV6nKdmPcPE5U\nGy4ZLCJRUyvOzPQTfbZFfnfqLDPfryR7Qrj2Y6YRlXgIB6IWhKXQgfPpJwy5\niYasEg/3NDgG9iRy0Uy9q/RZ+vgp+jMVx/deO9dF5PURFBs/AYSQOIvTVFK8\n5vtZHvbCeACiCWu0HiqO0UNcLdjnpnRFc63tb5UqfhJfxgBRmoT/8at1gspp\nZcSHCrLkUQh1fIhi0HRQoHYyesNRXNJ8u0VIbSrKWi02SubX+y7hncEBzJSX\n7Q3f7IVPFKrmOmUFe2URJSO0gI3ji6qBqsPsp61NbJ5m5OcxAww0SIEMzYA6\nOmZ09A7Qik9PX2TYVSUp+jJPqn+kQtn7YjL4rFRGqrLVfgNfZxRIlCDEi4Xz\nK7ME7HwnYr40vjwHPQPh66bmtYCOUrsacX4dctl3FQajBXCVLU0AkpSc10rR\nYiPJmPEyXcQQy/uMJG0KvuoO41yUsJ3ZfeIbUSF3OIMtbPZ8bUbMCqmzdkO7\nzm2mwwGFR6TQT2kuPemm28FabsY2mut+7iYgdhmh3gfQaXTlEuOqz4MZtnUr\nH6fwc0EBK48yPwtIzN4P/ENl63bJYqG8QnXcJOZHsVS2439p1VqwCAb0bRS9\nkwhHHpAhcEUBGvDoQcqTdJ2fkvML5bHkV325BbftuUd8wSuZnBl4+e+djgD6\nQlZL\r\n=s6LQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHgrTnvx1KBHGJm2YlqZ1duXQ+Dha638w3p86ilHjdlNAiAuVJenO7YGxXSNiv7k6SNJ8uZXDwr33oj6SA7iGf/F0A=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.4.0--canary.182.cf66a112b36dab90ab2433d7f41b7dfec0dd4cbc.0_1629963734059_0.026358844532186065"},"_hasShrinkwrap":false},"4.4.0--canary.182.21cffc4226831c7ac7a1163aba1cdfbe390211d4.0":{"name":"@sberdevices/assistant-client","version":"4.4.0--canary.182.21cffc4226831c7ac7a1163aba1cdfbe390211d4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"21cffc4226831c7ac7a1163aba1cdfbe390211d4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.4.0--canary.182.21cffc4226831c7ac7a1163aba1cdfbe390211d4.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-AYkwJDR56Mqtk+Q6HznmeYG+7PckPiqwjjV92GTNcgpP9sTQoiEJvMO3izEnmKY32z976hhs5WoRgfmEd+/e6Q==","shasum":"56b4873b581a9033eab475d1866e41981e4a1775","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.4.0--canary.182.21cffc4226831c7ac7a1163aba1cdfbe390211d4.0.tgz","fileCount":81,"unpackedSize":1754783,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJ0atCRA9TVsSAnZWagAA1TYP/ifJ4ZSQOdgWNEA3oJFC\n5R+zk55iQ7CsburLpzXiD/r71a/afzR0mGEA1soNy2KSk7I1Tz2+HwWG3rFY\nup01fMRwQb+95flW4lQclXNiTCBvTDY4KmPAXR6xzTIoFlRs04z1fJNpEHdX\nYvYOfgjs4xlZjGEFWjG19hn+wnNheoLw/E1Lg51OMun5XwUtxmeT3fhpKURe\n6TVsBDLcumvTltU33cXNhuTG2dYfCfrYAPi74EHrGz0kOKYyCFk9OtbIpLyT\nfT5WIgRiJvXAdODvmLjEwmyNIj1FY9Y+FvFJQYzbdxccmJFkvu3cE4nWJC0V\n3TNS0eH1P6D8oL99S3NJ8kc2FafM8rwyZJmTWdiccngIZDIh4QWjEmyXmYuA\nIixnvDmnHjXWpxfLnPYrqdgMBsn1gZrW8kAS4nH9uCZ/AII0anbyMqIP4ZfX\n4Y55QzMSD6QwujklQU5C7CQut/G6MhNiHsDJfjphPCyc9FRFOrIjvEmDUc2m\nXG/32tFWgh2ieUT8NyMKlBlSuvH/nvtaVIHrtllffSRxHI8feFOA+Cooqhiz\nLZySImN2j7zGec9V084grCRq5K199M2yVeWA+1UeozKvUvajgpPJ+xDeoaEM\n/vbP2YgqVFvlO3yPIy0VYkZXw0wTdattOPIrMKH4SuLPyYPRN5S8OaMx+Zrd\nfUKl\r\n=Uw8j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEMgeXHPagCwvrEsVHW0Ivj2Vai0gxSBixCDry+nByYBAiEAw8XBiZd9wkkQFnziq0Y8zdiCzWxYIcqkr106DoGLbA8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.4.0--canary.182.21cffc4226831c7ac7a1163aba1cdfbe390211d4.0_1629963949141_0.8211134221295961"},"_hasShrinkwrap":false},"4.4.0--canary.182.e36c10312312d562c41e5c89fb469bb515a13a8a.0":{"name":"@sberdevices/assistant-client","version":"4.4.0--canary.182.e36c10312312d562c41e5c89fb469bb515a13a8a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e36c10312312d562c41e5c89fb469bb515a13a8a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.4.0--canary.182.e36c10312312d562c41e5c89fb469bb515a13a8a.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-hP/jG34se/KpRuXxCjYuZFE4vAt3HsGbAEMTkgQFtgCuPCYpbUqV10Gl/HNInSgWIR5k3sN5z51L+1V5FFUb1w==","shasum":"6670f3b79bf48f8e7b06dbc722fbdb0816486e90","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.4.0--canary.182.e36c10312312d562c41e5c89fb469bb515a13a8a.0.tgz","fileCount":79,"unpackedSize":1754251,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhJ0bqCRA9TVsSAnZWagAAbMYP/0y16VjHu6OHFoXYicxj\nQuP1PGKYqQQIgyZV6zmQmDxo56A2hvi/w7E0COaMfbkIo93O3QUagu+oKjYN\nnT0/eA++7ztVJezMI4i04IctMZjIIQj04GMD13NHX2GNxvlHKjet1sA2hkVe\nu8z1dFWKVZ1K/QWnZeoKyKOOWS1UcpaKYdsz4Zycm8riVomCqeiMHmg4A+eU\n/A+zpZPBiB/IMBrvITxKL7JCG16j/n6tKp0N+Jvqb7MpFUtTrG4mnDLiIDh7\n+M4zLJY0NHMR7hY+PKJF3Nn+vjOw45M6CPdkuI5q3HBPARqF4jHrsJSaSMhi\nmO0czh6On+iIu776KhW1UE6U0LapxTnFgfT/eIOAopxdnRsQS1lB+rqj7U9Y\n9GyeSH57bupG2uaOiJA7F7cwnK1mhxY13VhwGHtCpy+xrhb/ub4PacdFlOVr\n5pH0jnTCUydNiIjNnazhXjidWRNEs4zH7M8+gGoSHAB1Wwx1G6i2WGruV8pG\ntvdK9gLN6FGQDoDk01MtMOQ9qlgHbihMez9z2zalzVZ2YTbp/gdLTzBfUJrT\nPf+ZXo2RTCiL9pcxc8qZD/Z/Nop8NvzBLOKKA7vMjwfYFoldeMlYMcaF40OA\nBSQb8lhC9jIutuniGDfNSyV7bNEav4a6Ozi+wu68gt4GG3xERoQ0FjeVNorN\nufy+\r\n=3X1N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDcbx6qjZVGO702+Ld7X4z5oDJpY8mPwTyWjBKyfBOwGwIhAOVdQkOeiP52XWJsSF2PQFLX1S/S0Yugcs4VyfOhPPZM"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.4.0--canary.182.e36c10312312d562c41e5c89fb469bb515a13a8a.0_1629964010789_0.7045187187360507"},"_hasShrinkwrap":false},"4.3.1--canary.184.09debcd03641afeba404b8c6deb99ba2d1a43694.0":{"name":"@sberdevices/assistant-client","version":"4.3.1--canary.184.09debcd03641afeba404b8c6deb99ba2d1a43694.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"09debcd03641afeba404b8c6deb99ba2d1a43694","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.3.1--canary.184.09debcd03641afeba404b8c6deb99ba2d1a43694.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-M+kBjOS1I8txwn2PLtkRfmKydA0HShy1sjW21+o2t4599nFUhOrPvTO+AqjnuBrSch1mA98QsklfN0y093sXHA==","shasum":"994f886842c9a64d00fe5bd4de8e29c90fe6a3e7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.3.1--canary.184.09debcd03641afeba404b8c6deb99ba2d1a43694.0.tgz","fileCount":79,"unpackedSize":1754228,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhLgrWCRA9TVsSAnZWagAAC0gQAIEh2dZEt3MsUXvoG6mN\nzWSNL4flDFV9BLW9txR/dxhWVyG4b/A4BpbdQPWWBYxOVra8M8dKvwGUwc8F\nYLAiZAVFzrB+ZsxTAWVW1xlWcmC2mvDTA92Qz1STq50r1G2GGhRtH0DX7mGe\nVaRJMNrkd4nmUSAJcuxFF8T19yf+8iJid84yR0uSoUXOXDJkEKHAQFmhbDfx\n8TMGp8OVICYMhgyNTVkF59BVvhJrgoRfYLyYspM5b49m9tfHH54BzjNPYClZ\nsvnbJUi+Scr7l3r2bj4reTl83Vzq0+2a/JtVlZ0YDyRvslWdX26J4BuWOhKQ\nQ18VejHziVtkypsJwxDemFTp8+jUgjUM3FeinrVxRGymzvafI2FzTLvhcecs\nc18lrBlHsXfHza6IIrp6rFCqCp9ctjcq1rNwQkKrAPUnfvx0BKnRfMTlpX1R\n3QmzkT/sb1avFiuFz7ehs2sC3Jg+DqFhZc0qXcxPDfWoaGaJRaGnefRGF4Lm\n1EV2V+z602j/rebAJE+EnauF/ffyqPRGx9woLh//ctIlDM4xJh8yWFt5y3Z/\nG5QcQsz3EMi6L0uZjusG5mv4R6IHHgO6vrURLw+VSxUMRlrD4JlccvXr6u46\nJB0gO97ItluSuMEyexJyS/58LLW64kPhBe4tCFynQRwd8QvQe8g3tzCOdzFb\nRuw4\r\n=rfK6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHiqU166jQqqW8CIIQrUCK0X8SmK+coOET0G0eGtgaEIAiBCmanTtW/U5EQ3dSDHbbOPZ2H/lRiTeRcKniE1IIg3sA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.3.1--canary.184.09debcd03641afeba404b8c6deb99ba2d1a43694.0_1630407382195_0.465332188631318"},"_hasShrinkwrap":false},"4.3.1--canary.184.17094815990a302a26563229398dbee0a7530701.0":{"name":"@sberdevices/assistant-client","version":"4.3.1--canary.184.17094815990a302a26563229398dbee0a7530701.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"17094815990a302a26563229398dbee0a7530701","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.3.1--canary.184.17094815990a302a26563229398dbee0a7530701.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-Ia++ZqCWM1uEyIWZ4EaKb7YZE2l+nYSWrUrvaYIvaFnoRwame78bKBi679hJ2bFMZZI8psA1SDv50e6SuXMiKQ==","shasum":"435d09d1b345217b00a5941b017faf761cfaa437","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.3.1--canary.184.17094815990a302a26563229398dbee0a7530701.0.tgz","fileCount":79,"unpackedSize":1754228,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhLhfUCRA9TVsSAnZWagAA6y8P/24X113NvQCKYR//VhD+\ngkYuu7SXOtG+cyIWPh4KL09emi3kzC35XQ8jJYVAbCb/XDy1oVF/M5XBjzK7\n6Q5JHmZ2LANng/wII5sZObTHwXmC7DER4m5XawSVIxQfNtzGZzIKuTQgxDBI\n0ciyaFgZbZ/x6td+64S2lwjErWqTAsitdurozyggCT+ekFLWOsbIldkuO5ev\nfx1gVu3y8ZEC9qgW8i4UnOKf21HitmmaBv3Q9xj8+rJLntYHKK+qgujpFMc8\nNbYnl7evi6YTOQPHlXFs8inhWt/MskRVKoGbaQmlXOTLBNnB2H63FtMIQ53G\no0MnFRTZgK1vpJNXGS/ZRjg6/w+M9Zia40nzkygosHr71FmxDwsattyTTfBg\n2wZUQS683EpWk+Jc2vpQ4KXg7uayWSKvpz0a8ZUWKoUNOZo/7CvJNPmJA+xg\n0BxqCKwHq+qiSfLoryoFPvRhHWnLfLZrvRVXqQjt6kjxnKEpF7cSg74cU7Fk\nvqJIBtECUZtyM9IEIFa8HknM2uWMil3VO6EkC25B0CBFYh2ZUf763TqU/KxB\nVJci4w3YX3lYCi0ccLYtHjjFb4isZpr2GEBq6oWvqB923YRFytN6A58bHR9+\n+QP5oLnvmK109r5Vfbg1N2pFKaq+vIxG5g7euQw+hjBAvaJC4qkb/K3OPPMm\n1Qb2\r\n=BdE6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF+iTUhOHf+Gt7L2W2YrYD2ce5LeVtCk02pCfmf6UGPEAiBa54rP63z2WVVnSoi3EGtoV6HjQk5jtsD3UVgaMxKjlA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.3.1--canary.184.17094815990a302a26563229398dbee0a7530701.0_1630410708392_0.3794190555167227"},"_hasShrinkwrap":false},"4.3.1":{"name":"@sberdevices/assistant-client","version":"4.3.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b5bc45e2e969478ea9928ece88ad1a54b0ac6f47","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.3.1","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-VEtZZ/ZoYUOgvLDWvfNA07X31s17WUpY+CtYOpsdIhyMx9hmHKjLqLe/H9/zv9oJlAG9RO118NaIZOrjL7Xo/w==","shasum":"ad6caa22de12e8637652ce22b963e9cee6accdc7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.3.1.tgz","fileCount":79,"unpackedSize":1754330,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhLhkNCRA9TVsSAnZWagAA4cwQAJQ93nu/dEKAYNVX6eDK\nbo/isZjjsuupEQ9/IhESEsGnaS78fGmLboif4e81m5RZH1skuC5WTuK6cDau\nBiDZi3X3tDXyGcX1G2dCQ/2QvEDy5yKYynYHX//+1YIU5z1Cg1tI/FfyuayL\nWuNaC6OlhPUaay/xrvPAP53DN+wMnlQ1HZ44w0N9iVCk0p7MaW1QtTc2GMp6\nMdnUZYIgG21HDB7t/pymKzG0Th20zIRPINkZG9ZPluiYY73cLTwnOHt4s55T\n3urNWipsyt+iDPbP8Dp/rVVzaLDZxgP5wAE7FE/Lu3Ctp6Vfil8OTXFfJ8Dr\n7ABE6TE+ss4GmOn9Qs3+EPjXDSFKHftHbpoAA5L8332t1NvuL2GUx0BWf9jZ\nWPT7YyTyefMMiAuCb4hgEZRBOsAbgMlYlLL+u7oRlG/G/UQRva0kvjqErtQD\n372Nnq89YzNmmy+4wTBnvjRPfoZlRhr2vbNFv1VjXPrRxNbNYSECs+bH0s47\nLUPVmKSl41KWKQQrnY0j2kMYyQKeeag7IoFtHwIbunsmIa93DITx4QhurE3i\nhner23+M1wjIKjw4Jg3h7YqmIYczgUTh6siwUZX0+1sidYVV6mitkGj6g6/j\nzQFbaOMUadTjNGRkAIsNMDBThPRWZJu1qI+e/MKUG8D1P5G5duW7YsB+aQva\nhC+A\r\n=nCle\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCj8zuiqFrntPDDTeNYBdnwKasG/xLwElA1KMEjZ2mnLgIhAKZe8RE/778Q3FjNDYLF6y2ZlBV6uAw1RvjrIuFSDzxJ"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.3.1_1630411020711_0.13564686275480375"},"_hasShrinkwrap":false},"4.3.2--canary.185.04491ddf44edd6a34c30fee7929ad695528241dc.0":{"name":"@sberdevices/assistant-client","version":"4.3.2--canary.185.04491ddf44edd6a34c30fee7929ad695528241dc.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"04491ddf44edd6a34c30fee7929ad695528241dc","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.3.2--canary.185.04491ddf44edd6a34c30fee7929ad695528241dc.0","_nodeVersion":"12.22.5","_npmVersion":"6.14.14","dist":{"integrity":"sha512-3WThk/MqfBs7HD2Deqg0Gmq3dhG1hna+wizxoygPZS8ZFF6BXEVXeipQB95DJmMwpOJtdolqpULbWHvlZSJ+5w==","shasum":"a948cb68a2e4170e9a4c1ec7b15df203b27515e3","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.3.2--canary.185.04491ddf44edd6a34c30fee7929ad695528241dc.0.tgz","fileCount":79,"unpackedSize":1756747,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhLzswCRA9TVsSAnZWagAAhKkQAKPEwmhS9oLGsRE+45+z\nAqyEbOMDdKpRy9DTyL6yG1xjedrWYhWNkeuoYzr7bnM3iRkX36NyPa6kTx2U\n6rtQ9G8he5ehnZ1WY4oGFDFeXCjwOsH6yUhoO1NWUOXTUAY08UXR1lsPcc9e\nSyVX6EXCgzW8zQG8yq2vNvxlpn5q7WqrG9/0kasD5Qh2QjhYunT2dgYIF7mI\nLLktJfxZKwu0doKQddV1RuT6VcGKrdE+eePENGF1gorPZbTxVzaISRFEnTha\nF37NREDBKEnTt1qnWPuhv4aGhIFHkmEcGXUKWD1aQhBrOBBkcX2Du1cYnzLw\n8of+xKMrXN2J/AEBiPy6HH38QiBwsquj/B1ds7zcfyNZJEg8vZ13B4qHOzGu\nlkrpBCap8+6w055YmqI75Bx6BWrmSsVL9485sCHLQ0Nop0ExdScQel5hMprG\ns+a+vlU8eTxSrmE1pKTkf6vOQ9ycUkD3szAGDUhNeAAKi+PGQdVqbGxiy6Iq\nyU/Jmm0MQC3QzbzZjO7yk9ciu1PLuGePjUoE86k+3y2WhwI3282oYETDDwDt\nktOuYIC7UM/Ng4xnZA45yCI+iFQ6BuPce91CcdZB7DwoK4YqcnELEUxCHt4l\nMoEXkH28u6OWR4TGi+R2QzzIPXXuoIByrN0djnIovaIn6vn4JolHsTwt/4+k\nDHbo\r\n=uVvu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCXnxh7vdiNzAjb8LHFnJPyNT0kocwPNR8mezmTe/u8mwIgFOO8/mXIJCnUcoMTWD7tR1XObuY+i8eQZtsd6lDk8g8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.3.2--canary.185.04491ddf44edd6a34c30fee7929ad695528241dc.0_1630485296009_0.9835580532175963"},"_hasShrinkwrap":false},"4.4.0--canary.186.5f743be0179c80a5a71d35e1f667760df5aac22d.0":{"name":"@sberdevices/assistant-client","version":"4.4.0--canary.186.5f743be0179c80a5a71d35e1f667760df5aac22d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5f743be0179c80a5a71d35e1f667760df5aac22d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.4.0--canary.186.5f743be0179c80a5a71d35e1f667760df5aac22d.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-pOX5gDAU0XbJtEooNc77wrM2veHQh69PL5MjTRfduD20wrlSrp0ztUUrm0ChwS/q9fKiyQHroHN+t+lcMLvf0A==","shasum":"20f93e66e652d34fe00d0633cb001ab310c23c23","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.4.0--canary.186.5f743be0179c80a5a71d35e1f667760df5aac22d.0.tgz","fileCount":79,"unpackedSize":1755524,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhMfJ8CRA9TVsSAnZWagAAtfgP/3SPvg8nRGwl6bOQqvu8\nhWk8xaHjSBqc0HvWHZquWPKGpx4L1tDfUsFRvgDGv/tyOe/vupb+BH25RonW\nlxvm1gqnvuziL4hPe93tTVw9JkI2BcGkx+Lwny6BLRCoc3K+SwAbR0s9CPRQ\nfsnpguf+vMFfuyMb8J3n/BNYfAGtOWpRkPr+rO/Eeq+++2qTRiGfoN8WmAA9\n+OQCqWGG0HeMG4I5jj7pWHLF0PQa3cq/YUkD5Zhv0vQzZLLiGgLYL81v4yce\n/qCu4zZzBbClGqGItHaQWQbGGneAfbzGdpq3ZDJ5Q64HUSB65K3dPJAXnstH\nHERZBvNzT4dfNUjO3msluvcyxbIyiPH0W7LmwtvWpE08RyCZXwa/nE8LUxxj\nbmRvfYXtsnAz8yorEOlccg4y921QCEh4R5Boon5RrXKlivszIz3C+nUXyDOs\nSlI0zcSw02PvjWUjkYsjGkbdo8BkWs6L3fSY0KLRyel/kmhmXAOOyYPvf+I0\nO7iM/u+8TFbKJ3C9TV1Mxh3GPk/gYn5uDZj6HTwnwBEtpzebfKt5iRMKj1LY\nWXJuuF9N260+J/y38VDzFq+wUcQ+4jBeGstjTuqDkrZGdY3RfWCVb2bHPIDY\nUBmErT0xJFt9cOe93yi8ZwBwQi3OJY+l/O0AW1v4Es6jf2rFAqdrW2N6QCtE\n8xYg\r\n=JaO2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC8uVsRG7RqoidAsgK08GLzBccgkB8y/1rBw8Ppl1nqtQIhAL9sPltxfe26ScqlyJ2m6rOgFAAOuEEmJ4PwXfzZpZs0"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.4.0--canary.186.5f743be0179c80a5a71d35e1f667760df5aac22d.0_1630663291917_0.3260324081818182"},"_hasShrinkwrap":false},"4.3.2--canary.188.e6d89c365c618abdd8cdbadc17ca798d30220e09.0":{"name":"@sberdevices/assistant-client","version":"4.3.2--canary.188.e6d89c365c618abdd8cdbadc17ca798d30220e09.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e6d89c365c618abdd8cdbadc17ca798d30220e09","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.3.2--canary.188.e6d89c365c618abdd8cdbadc17ca798d30220e09.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-RWOYL0cBQUI3Bo5x8Ndb928M+YH9o7PP/dquQYr9/C6+SREWmZHXCleqCYB4Lsuz0G5rlP9B12g4azC27wPmbw==","shasum":"2972fc5ef36d993fd19f8c367d9ff858659c6c0f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.3.2--canary.188.e6d89c365c618abdd8cdbadc17ca798d30220e09.0.tgz","fileCount":79,"unpackedSize":1754550,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhNx41CRA9TVsSAnZWagAA7tEP/RYlY+mR5P373OnVYcIi\nB1vzQkDveeh2SmFXPDXMAm1zj65ZNhFFN5Qo0ZYhO4FE9uszFQaYB0+ulapc\nrJ9GqoToJ//9RHR+EKb5f2bAODBfNR0JuYxAJ1aFSHp8E1+BR62a/+FXTMWd\nUEXWnEZRw69cUqLPztNEaOth/sYbHLHqXg9eZunk/XiJuZBI4TfhR325Ujok\nWKoCT4iOVD/Eef1w8uFu/Hlypq1zp8LBFv1iF+MBrcOgt0eKmhMvzBrw61Ri\nRVY8DRW42yBrE7Xl4+zohTo+LMr+dts8tPhu3naYNsisx+9hIGHgzX/b5j/U\n/hOpj1m/meYGtmDF0AikLk0cdgABHJhRFmutlWZ/GyMobbJhLIaYspzfAndV\n4uygSSKalTJ6JU8WEJ/6C4adGIb8PFgBEpOQpceVz6ibE84/ZjW3/tQCbhN5\nJMuCDLPzyDU4kk+s/7XqSd1dYs+/q9FppU7S2oEz4nRszqSDFZgVPd7piZUW\nzqk6MPP+rpobwRnWf9SzkYUOHOVhMzHMxu/kpN6HKFcDkPqqaZsQzEutI32r\ntRqKh57uRRmHlEV6GK1M8tGy8mZMHqpqahqKM6CM28tJOb6zvTstvIE9ZxQN\nwT9OQv57aNwrjCeKKxb4mEIJR/RcA7LEXjD7zLAL+h5QBwuZS9kcPXpz1mIF\nuVEZ\r\n=HPQL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICJHAwdbCV7QJEEfdvvs9T1lHgPh0T8wOsHr+4vc6TzIAiEAuzLurrh1R/neiFbFqWcZSMWzAadhh6JTjPW3Poww+QM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.3.2--canary.188.e6d89c365c618abdd8cdbadc17ca798d30220e09.0_1631002164867_0.22439200088335443"},"_hasShrinkwrap":false},"4.4.0--canary.189.81b4df9a8673dbbdd73d8e65766a64d4fd575789.0":{"name":"@sberdevices/assistant-client","version":"4.4.0--canary.189.81b4df9a8673dbbdd73d8e65766a64d4fd575789.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"81b4df9a8673dbbdd73d8e65766a64d4fd575789","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.4.0--canary.189.81b4df9a8673dbbdd73d8e65766a64d4fd575789.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-+Nweja84E8H3zAsbHprD8B55AjewjDKfaq4MZiDwUw6wH8t1DKI0kHeDbfYQoZbClTEXbtssBokqKUu/J+QiHQ==","shasum":"4d878273c0295b3c4173e3dab66a2ea1b5c7b4e7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.4.0--canary.189.81b4df9a8673dbbdd73d8e65766a64d4fd575789.0.tgz","fileCount":79,"unpackedSize":1756642,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhN2PfCRA9TVsSAnZWagAAA2YQAJ3FDSO/7T20tS0lSlM1\nryH2wrKw9RwKTNsGY7KES4B6IFakNEbVOc3Sa6F4JdxhAKw7/QEX2YHJY7eB\nL2S4AZfSOvb48GTAWcmbrbihrwlWOtitlJBokzrjMUaIx5CIKrovF4Or8n4P\n0rQnIh+j5WaiXulgD2xfCkeCW0ttAsDnRJk6GXBvcZlUNIUWwyLObNScj6eS\nSAA80ht4MxF0fZhDOHgUjVv1yjeUvszRWADF7gQAQQ4n5ILAL847hftFW1Gw\nx4gSWiYselZp+15TogJlreCQXBB5IJtIegraLRLG6HXNlZ/OgHrteBjQqHvo\nYRtEXbsYPX/quCl3EjG7RYAqYRS3DlGVfw0u3otFxmGM9Xm8IjLYoAYlfcmn\nkukYuG306SiObVIw5FBoWUMhVHWt2KKq/J5ez7bvXrpEj/+UaJfmITTKT5MG\n7fahLbGtXzQGmmI/yg3LgBL47JyxlccVGHPPJ7dXmW0Qbe+XYZbR0l/cpMHn\nVzP9eib6Fu0D5XHipps2Gc8zZA8BFLS8yrlqGDLVoMW6ApW6sZH3n1V1MovF\nMfiOMlk+FMjY1sVeJZT9zYyes8bMHreS7rNrfLA1u0LDCxhemzS3sUSE4b/q\nPJVvMQHnVxmtWNCGhI/nq/1ZubIQcnckY6yjcFbYY7xySOBo8vZQlXtmVwSh\ntTLd\r\n=2yBT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDC+I1yMb5M/v807Debn298YVWp/TP3zqJzc1xGkWqBnwIhAJ/RbTV7GDI2NV+sd4dzttZcT6J5phtOfkj2H+WtirZa"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.4.0--canary.189.81b4df9a8673dbbdd73d8e65766a64d4fd575789.0_1631019999242_0.7823699177827477"},"_hasShrinkwrap":false},"4.4.0--canary.186.2ab5d7d316a5140309b676be55e935ca8b306853.0":{"name":"@sberdevices/assistant-client","version":"4.4.0--canary.186.2ab5d7d316a5140309b676be55e935ca8b306853.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2ab5d7d316a5140309b676be55e935ca8b306853","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.4.0--canary.186.2ab5d7d316a5140309b676be55e935ca8b306853.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-23whPdMZSekSUIbZLDqZQJDzATnGdqJOBM7Fyl8KZhl8+tc2oJUgdLLNFnCjdL1+Fhmj10HDbPdhHpGBD7UmTA==","shasum":"fc7e1e0d5302cc6550902915dc190b6154904e60","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.4.0--canary.186.2ab5d7d316a5140309b676be55e935ca8b306853.0.tgz","fileCount":79,"unpackedSize":1755560,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCZNwEm83TjE1InUprQuIQE+9B+UdIFHsQheNDhA8upewIgLLOuzW6uZ+uIHluuxEbQV7dLZB9WaqOA/8q9OLEW6D0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.4.0--canary.186.2ab5d7d316a5140309b676be55e935ca8b306853.0_1632140114554_0.3871524966664168"},"_hasShrinkwrap":false},"4.3.2--canary.192.b200bcc410a04876f0fd5b091e4e8feb4300951b.0":{"name":"@sberdevices/assistant-client","version":"4.3.2--canary.192.b200bcc410a04876f0fd5b091e4e8feb4300951b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b200bcc410a04876f0fd5b091e4e8feb4300951b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.3.2--canary.192.b200bcc410a04876f0fd5b091e4e8feb4300951b.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-rfl/PXtt4+a/2RC6bjOrFrbTU9+vUrQ6Slhj9urX9qnCmMAwcbkA7cXvV7gh2HtWUHnqpM7/Xg8r8qIaep5M6Q==","shasum":"5ab72e1fe9be571275ce0e7fda767e5d613be69a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.3.2--canary.192.b200bcc410a04876f0fd5b091e4e8feb4300951b.0.tgz","fileCount":79,"unpackedSize":1755998,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCGURDYyzF9XtuVw5s/CLeeJGDx15SFxi741jmYWJBW8gIhAIG2UpV6W7UOYicPNEZszaTD5+cIWPvJ/HgRrPHzzJhM"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.3.2--canary.192.b200bcc410a04876f0fd5b091e4e8feb4300951b.0_1632217142005_0.6955632178658897"},"_hasShrinkwrap":false},"4.3.2--canary.193.b7196bff35292da813f7f7b1afb24de4173edd61.0":{"name":"@sberdevices/assistant-client","version":"4.3.2--canary.193.b7196bff35292da813f7f7b1afb24de4173edd61.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b7196bff35292da813f7f7b1afb24de4173edd61","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.3.2--canary.193.b7196bff35292da813f7f7b1afb24de4173edd61.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-1Nr64JgKAfg7J+wjUXNIeAjLu/QTgeG1CSB1bKqj7kQebDhOaxzzUrzJTrjfbK+IiIwL4Jj6liD6EFq2VcN2Pw==","shasum":"6e77d8c74b852df8f3e9b2ec90f74b7d1a91c994","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.3.2--canary.193.b7196bff35292da813f7f7b1afb24de4173edd61.0.tgz","fileCount":79,"unpackedSize":1755998,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBJk49Ka2DDyWVNnHMLhgCoaDZMXe+l44/UtiwFTcHxiAiEAsWsHc2Jy8dii/5fstQdWwY8u11Pv2ZIgMaH2ymr7kro="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.3.2--canary.193.b7196bff35292da813f7f7b1afb24de4173edd61.0_1632217367717_0.6713574389806736"},"_hasShrinkwrap":false},"4.3.2--canary.193.c24a5ed99224719f5a27a3479153e498711c53fe.0":{"name":"@sberdevices/assistant-client","version":"4.3.2--canary.193.c24a5ed99224719f5a27a3479153e498711c53fe.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c24a5ed99224719f5a27a3479153e498711c53fe","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.3.2--canary.193.c24a5ed99224719f5a27a3479153e498711c53fe.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-I5sngndVaX2Fnk3owtp6FQpepCvKq0OuT7l8OBFzA+vHs/k6Mw5T9rtq1kQctGEbjBj5hxa+OysnJGHRf/8+Zg==","shasum":"7a9420e9e005fe75c3127aaf74ed2094cb8745c2","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.3.2--canary.193.c24a5ed99224719f5a27a3479153e498711c53fe.0.tgz","fileCount":79,"unpackedSize":1755998,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBIqGp5/+itLN2a+QCakS6mWSwpw+hJzgA94rTzkHhKrAiBmC9uz4RGTOaxDEOCxh1X0Ub6CDC4J0VTQhWa3gaLr9A=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.3.2--canary.193.c24a5ed99224719f5a27a3479153e498711c53fe.0_1632227813599_0.1938116885269363"},"_hasShrinkwrap":false},"4.4.0--canary.195.dfbec69d562d8f506ebcfc17c10840eb27df41a8.0":{"name":"@sberdevices/assistant-client","version":"4.4.0--canary.195.dfbec69d562d8f506ebcfc17c10840eb27df41a8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"dfbec69d562d8f506ebcfc17c10840eb27df41a8","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.4.0--canary.195.dfbec69d562d8f506ebcfc17c10840eb27df41a8.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-b/HPt+iEHilih78v+rcmstbqKNRaVYWFRmuvWn8+g7QXmfL2KzWNuuuY/La5hr+4v2UzhUDkI6trXG46sLOQng==","shasum":"3ae8d78d2db3704254daf9e578ce24f4fca3ad6c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.4.0--canary.195.dfbec69d562d8f506ebcfc17c10840eb27df41a8.0.tgz","fileCount":79,"unpackedSize":1755043,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDfqcZ/dca04DbCS8uH5BaD0WmYiHj5g5UdHhWtTbs/SQIhAOPWFclENPi5fSGuA/U7f3334fxhG/+eZhRIYRsz3xPf"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.4.0--canary.195.dfbec69d562d8f506ebcfc17c10840eb27df41a8.0_1632237277678_0.4080952785204679"},"_hasShrinkwrap":false},"4.4.0--canary.195.3a667264893389b853b70286632c53c4eac5b11b.0":{"name":"@sberdevices/assistant-client","version":"4.4.0--canary.195.3a667264893389b853b70286632c53c4eac5b11b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3a667264893389b853b70286632c53c4eac5b11b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.4.0--canary.195.3a667264893389b853b70286632c53c4eac5b11b.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-q2HsqmaJ4DWyr4PJWwzXoeMMtKYeZPaVTcHMjwMMv85k1P0ge+RpJYSnmhSe+e62PnmMGWAfjIKhgeWqulHstA==","shasum":"856fd75f5291481a0822e07177731ed86f256713","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.4.0--canary.195.3a667264893389b853b70286632c53c4eac5b11b.0.tgz","fileCount":79,"unpackedSize":1755051,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4gGtw3cvqkw6sucE+Fg1lOLFf68eRS3/1W7sQzwBYvQIgD2phyx/mloKcOzbKnVbA4M2aE976dvSvAgjw0sFUZRM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.4.0--canary.195.3a667264893389b853b70286632c53c4eac5b11b.0_1632294769796_0.9243495740918721"},"_hasShrinkwrap":false},"4.4.0":{"name":"@sberdevices/assistant-client","version":"4.4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1d906926a567cbc9cb97e05a27869e38de659d76","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.4.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-oaibkWQeDIPJMAFVvbNTJ1SepiHlWNzwrPioPxJaDGOm14qCALvcOvIAQB67AQ2CtPl2rQc1g8CQoOdxqYKZnw==","shasum":"7802dd133e4ddd449ea9fbae5642e714fc8d07a8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.4.0.tgz","fileCount":79,"unpackedSize":1755374,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCF2m0SJdgMGnmN8U3jrkx+zwlAlZKfz39J359e2LEtJwIgEw5Y7tMh+hVB/qCL2iHeVh935WxyFpunHfOrXKUauM8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.4.0_1632296257402_0.3173775902495102"},"_hasShrinkwrap":false},"4.5.0":{"name":"@sberdevices/assistant-client","version":"4.5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c70173c881ad7f03244f6efa05c20a0af07b6636","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.5.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Hop4epaqtztTIz4hrgd11yNtHhDL7/4LmDph/ZxuodlusbIpFYuyPIvukkFtqlW5q2+WUpcw06YD0xu8EB0xHA==","shasum":"ebe8d3c05957fb78769f672fb733dbc612f63995","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.5.0.tgz","fileCount":79,"unpackedSize":1756698,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCISSE4cT876thCDGTv+lwDM4PrnhztIiNTS2C1WwwJbwIhAP3s3cDyB8oDUTwE/LwCC0NOjiQxNeEvg+TV3mxOkWmM"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.5.0_1632297928457_0.017727440441791087"},"_hasShrinkwrap":false},"4.6.0--canary.196.c65771fe516f95f32eb9d752d7c464f190b2ec15.0":{"name":"@sberdevices/assistant-client","version":"4.6.0--canary.196.c65771fe516f95f32eb9d752d7c464f190b2ec15.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c65771fe516f95f32eb9d752d7c464f190b2ec15","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.0--canary.196.c65771fe516f95f32eb9d752d7c464f190b2ec15.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-6WHIv3VqXM119YkQgfP7kifYzZRUUw+9ncnF6BkxH9E6+X3tXPgv/2WXrhL413R71IdwFNc88E9QzB1ImGWecw==","shasum":"b8cb583c6a2198b58f85fda35547ca1b9a09c9e8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.0--canary.196.c65771fe516f95f32eb9d752d7c464f190b2ec15.0.tgz","fileCount":79,"unpackedSize":1757016,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGR7NCpLPDfL6HuAE72qX5BjttnUEvPqX6udRMAWavmbAiA8FYNXKAT38oW25YdIRAgiqjeZckmpLrJ0SYLyQG6VzQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.0--canary.196.c65771fe516f95f32eb9d752d7c464f190b2ec15.0_1632300161712_0.1992739628107163"},"_hasShrinkwrap":false},"4.5.1--canary.193.890a9bb05bac8ae1ccee50e96eaafa3b68e048e0.0":{"name":"@sberdevices/assistant-client","version":"4.5.1--canary.193.890a9bb05bac8ae1ccee50e96eaafa3b68e048e0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"890a9bb05bac8ae1ccee50e96eaafa3b68e048e0","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.5.1--canary.193.890a9bb05bac8ae1ccee50e96eaafa3b68e048e0.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-4WnMOWjNiqcUjDjUECKTgspMvXcaDY1fHDIe/XucwaB7NKILAM8IKAZ/YdE3dV2S/7O1d9vR3pJ8GI1ethwlWA==","shasum":"b32a7ce420e930166f5063bdc70124bce8a26891","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.5.1--canary.193.890a9bb05bac8ae1ccee50e96eaafa3b68e048e0.0.tgz","fileCount":79,"unpackedSize":1758366,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAJ1hxPWgJ2bPGLp5o4ktnLK0RAOvUNlzUVrhYCXjBL9AiEA6v5kIXQClh3Co+SzgKGvkjDxIKmVQp2WJfcX54D3sC0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.5.1--canary.193.890a9bb05bac8ae1ccee50e96eaafa3b68e048e0.0_1632304741752_0.08309756761463039"},"_hasShrinkwrap":false},"4.5.1--canary.185.8981adcb9163fa2d9b15c817bd1447538db313ed.0":{"name":"@sberdevices/assistant-client","version":"4.5.1--canary.185.8981adcb9163fa2d9b15c817bd1447538db313ed.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8981adcb9163fa2d9b15c817bd1447538db313ed","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.5.1--canary.185.8981adcb9163fa2d9b15c817bd1447538db313ed.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-FGl3LO3wWWkXHl+GKIoSdt2HoWVpj3ilKXZj7OkwqkrakOav4xbUaemi2Asyyk/AJgDrRZYVo+OR4MRMZxxFEw==","shasum":"4645d2186b45dfef0bbc2990a5f2748cfd656056","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.5.1--canary.185.8981adcb9163fa2d9b15c817bd1447538db313ed.0.tgz","fileCount":79,"unpackedSize":1758844,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBS0UXXgkO6sm//OJ3wC39tcg2+dlw9bceyW9GHtINHUAiEAtKTWoNBJD+Fvzb0FHbgEJOtPLnY2EVuC83xv8AFJPP0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.5.1--canary.185.8981adcb9163fa2d9b15c817bd1447538db313ed.0_1632314252183_0.19238206589711093"},"_hasShrinkwrap":false},"4.5.1--canary.193.c5303589550197df47578ce75ff49fd58332a83e.0":{"name":"@sberdevices/assistant-client","version":"4.5.1--canary.193.c5303589550197df47578ce75ff49fd58332a83e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c5303589550197df47578ce75ff49fd58332a83e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.5.1--canary.193.c5303589550197df47578ce75ff49fd58332a83e.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-jg0AB8rXaBk3MZh8P9rXva6YjrfaGpLJQmHsS4U+CN/LYNh3eMetKF6ekx92puinpx2B3kW7RG9BdG8zcUIh4Q==","shasum":"2fec17a32c2c3544f38d01083a68daac2ce68127","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.5.1--canary.193.c5303589550197df47578ce75ff49fd58332a83e.0.tgz","fileCount":79,"unpackedSize":1758471,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChSPPuqpqamcW7SKewdDvMhu1TNJdkqgraPAF7MKFXvQIgMN1NmVk/id7AXgHTC6XpyWFPjJNwCtqKwZBTAE/TmGU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.5.1--canary.193.c5303589550197df47578ce75ff49fd58332a83e.0_1632329105820_0.558553832765547"},"_hasShrinkwrap":false},"4.6.0--canary.196.056d4062ccef39e511acf5ab5ed7f888f313dfd2.0":{"name":"@sberdevices/assistant-client","version":"4.6.0--canary.196.056d4062ccef39e511acf5ab5ed7f888f313dfd2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"056d4062ccef39e511acf5ab5ed7f888f313dfd2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.0--canary.196.056d4062ccef39e511acf5ab5ed7f888f313dfd2.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Tr7Oj8pdXVZJREDIOOouavVGWGfPL+Ym/xyPVlr7+mH0btxmhKGhNeAAgrqmdcLpiiitd6jLkFL4DlIel3g44g==","shasum":"835288c5c974b145749e2bc30e2fe14c61271dc3","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.0--canary.196.056d4062ccef39e511acf5ab5ed7f888f313dfd2.0.tgz","fileCount":79,"unpackedSize":1757657,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGAB7ehU1r7XtGWnFSyyXIBZ+13DhYJwYB62RlBrch2HAiEA5LJqlLI4g3BwrJL/wPOvYe6+XC4McPpoNv6TOGqDahM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.0--canary.196.056d4062ccef39e511acf5ab5ed7f888f313dfd2.0_1632379837300_0.9277939323785438"},"_hasShrinkwrap":false},"4.5.1--canary.199.9ef1cfcb223d844a519462993d14c1b05ff78123.0":{"name":"@sberdevices/assistant-client","version":"4.5.1--canary.199.9ef1cfcb223d844a519462993d14c1b05ff78123.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9ef1cfcb223d844a519462993d14c1b05ff78123","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.5.1--canary.199.9ef1cfcb223d844a519462993d14c1b05ff78123.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-31B8hlnUhA8wweL8ZKBzhUyV9tWrdP2D0S0VAT1IniTmD7MsJhZUy0ppz0DLK+wIr6IvcAvIazt0x3lrBKvIKw==","shasum":"41bb19518a890f1cb048ab059e4fc116218f399f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.5.1--canary.199.9ef1cfcb223d844a519462993d14c1b05ff78123.0.tgz","fileCount":81,"unpackedSize":1760985,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB5iCwzwCBT+C98v601wJJTlpvrz1vzgEXy+ZxU/0rMJAiBra2hFJqMl3vvX8yv0ilRAJpJ780pPOX/hL8a8K/RPoQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.5.1--canary.199.9ef1cfcb223d844a519462993d14c1b05ff78123.0_1632470386428_0.3697324088397598"},"_hasShrinkwrap":false},"4.6.0--canary.200.74308e16c65dc3feb715732ad06dc277e4f4fb5b.0":{"name":"@sberdevices/assistant-client","version":"4.6.0--canary.200.74308e16c65dc3feb715732ad06dc277e4f4fb5b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"74308e16c65dc3feb715732ad06dc277e4f4fb5b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.0--canary.200.74308e16c65dc3feb715732ad06dc277e4f4fb5b.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-TsB2UqKySbHYePy5ndanW5BsjsakKMo4ocI+77G0jFk1N+Klk3JF5sRWCc+CF8S+5/e1Uu/ovxsN5xCjf0CoZQ==","shasum":"4ca24c5453336d71b2ec57ec38fc703f037ced49","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.0--canary.200.74308e16c65dc3feb715732ad06dc277e4f4fb5b.0.tgz","fileCount":79,"unpackedSize":1757338,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCVUaU+EsV9P8HxnxOsxEUvy5/X4s8Hj4/RFfqprJ9jMgIhANpUK9mJaXC7Rw8h3r7aQD1+aG8m4c2NZBJhZO2IvU0m"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.0--canary.200.74308e16c65dc3feb715732ad06dc277e4f4fb5b.0_1632725426148_0.4793056562528557"},"_hasShrinkwrap":false},"4.5.1":{"name":"@sberdevices/assistant-client","version":"4.5.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d921f17a5cb61fc7d7a19abfaaebdb816c85073e","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.5.1","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-P34riIoYHu/iqJxDfrMRTEwnTsbEhXWLXdB1ZD7Sesa2R9N/UkusTY2DqlYVQael+paQThD2xQTU0BACLjXrQQ==","shasum":"1356f8bfd569260c9ddb4eeb001ecb5cb2c6d2a6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.5.1.tgz","fileCount":79,"unpackedSize":1758898,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCsB83LQ4aJDr+hjZMZV+0/Lbedyvnt+VyiCQcXuACTsgIgbg4vqzsvbFT/5gdJG3p+R5/172oJqOjha30YHPvJiDw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.5.1_1632835855359_0.27504742923321013"},"_hasShrinkwrap":false},"4.5.2--canary.201.2e53cd28fded271007bb09fd7f88c3246186b7c4.0":{"name":"@sberdevices/assistant-client","version":"4.5.2--canary.201.2e53cd28fded271007bb09fd7f88c3246186b7c4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2e53cd28fded271007bb09fd7f88c3246186b7c4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Spatial Navigation](#spatial-navigation)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Spatial Navigation\nSpatial Navigation – это общее решение для навигации между элементами, на которых можно сфокусироваться, учитывая их расположение в пространстве. В случае веб-страницы – это управление фокусов между DOM-элементами на основе того, как они физически находятся на странице. При нажатии на стрелку на клавиатуре или кнопку на пульте фокус переводится на тот элемент, который расположен в соответствующем направлении на странице. Есть спецификация [CSS Spatial Navigation Level 1](https://www.w3.org/TR/css-nav-1/), на данный момент она не поддержана ни в одном браузере, но есть [polyfill](https://github.com/sberdevices/spatial-navigation).\n\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.5.2--canary.201.2e53cd28fded271007bb09fd7f88c3246186b7c4.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-PNIGVO+69KtM9kWdL+AEe1IL4j5NofUQ6DmRlg103vtkPF7bG7QR//V7kX4qblthICIDuh2WbyZf/hyK6aVQkw==","shasum":"fd5f0060ebcd0a0f8b0405bbc3af3b8608f923c1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.5.2--canary.201.2e53cd28fded271007bb09fd7f88c3246186b7c4.0.tgz","fileCount":79,"unpackedSize":1760236,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIADS/pCns7TGrlnM1keVUgJRZhT1ZO0A12HBtZ1YUMvBAiB3acvofLJlbpTJE97b7znn6wwn4Xrdlzox1JvAsnePMA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.5.2--canary.201.2e53cd28fded271007bb09fd7f88c3246186b7c4.0_1632909993235_0.37149975393573786"},"_hasShrinkwrap":false},"4.6.0--canary.202.38cc3d5a83baa8e1008463c218e63b0a3461ab7e.0":{"name":"@sberdevices/assistant-client","version":"4.6.0--canary.202.38cc3d5a83baa8e1008463c218e63b0a3461ab7e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"38cc3d5a83baa8e1008463c218e63b0a3461ab7e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*. \n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию. \nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`. \n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`. \n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n    \n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.0--canary.202.38cc3d5a83baa8e1008463c218e63b0a3461ab7e.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-WkkFZvEj7dV5wSdSEtUpkw6yODSOx1k8//p5RcnbBtmfQHgHULmdIVU1o/53d5XD18A5ramGGVMbhI+MPDYUqg==","shasum":"ca30f8ad9d4f17aa9cac2586612d4a765ae8efc4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.0--canary.202.38cc3d5a83baa8e1008463c218e63b0a3461ab7e.0.tgz","fileCount":79,"unpackedSize":1760459,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKYusDWsa6Waalo6pI/f6fayvFyDEf6nWZxvndEViX7AIhALdevANYDBYUdhGRY707DJUaOIAyW/CY1jkLg9sJtfi5"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.0--canary.202.38cc3d5a83baa8e1008463c218e63b0a3461ab7e.0_1632920032333_0.03312853946620664"},"_hasShrinkwrap":false},"4.6.0":{"name":"@sberdevices/assistant-client","version":"4.6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkg":"umd/assistant.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b0c86a9756de1d2379e26ca54f1cd7b1596b631f","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-lgxn0PUx2wibaBZQkQnsGMHZ1iJ565A5liTN8dy2sPiFzDRH0R1zCTXfecnlfNWQCdlpsJoN4iqRNYlhDOq5ZQ==","shasum":"0018c7d3c2c0d52e0cf6dcc148f8c49e407816e2","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.0.tgz","fileCount":79,"unpackedSize":1761712,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFRXmBza5+EW7jfn6nb5gq7kKFwSJVJdp3wuEv1ed9OEAiBJ1JaSsPwkGSP8w6EnRAQ8mqigH39GDt9JK9wZjK4oCw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.0_1632920717632_0.7241437919651497"},"_hasShrinkwrap":false},"4.6.1--canary.203.6d9b8b9274c65863d60fa88f14ef9aa84fc45052.0":{"name":"@sberdevices/assistant-client","version":"4.6.1--canary.203.6d9b8b9274c65863d60fa88f14ef9aa84fc45052.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6d9b8b9274c65863d60fa88f14ef9aa84fc45052","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n// Только для среды development (createAssistantDev, createSmartappDebugger)\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.development.min.js\"></script>\n// Только для среды production\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.production.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.1--canary.203.6d9b8b9274c65863d60fa88f14ef9aa84fc45052.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-S6/5Mv9v0SpPFTueFwyIcVLKJqHXQaICqtjkxtjILjLgqivRNEBhoMwPtxdoeqSjYxasllB1lzInValq9ABW8Q==","shasum":"6d685992942612d2d5561aa290fe72d602626a67","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.1--canary.203.6d9b8b9274c65863d60fa88f14ef9aa84fc45052.0.tgz","fileCount":80,"unpackedSize":1838950,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCAiaeZ7wPzV3NzTojGOZMfRvPdIPP3QRuSt9TXZ1BGxAIgAMRU9cWTkDYu8fyG/pXdTWkvYiFcn0CDZpmeWjqyf5o="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.1--canary.203.6d9b8b9274c65863d60fa88f14ef9aa84fc45052.0_1633093766022_0.877324546117662"},"_hasShrinkwrap":false},"4.6.1--canary.204.41bd367f728e700d45b989943a044800197ed2b8.0":{"name":"@sberdevices/assistant-client","version":"4.6.1--canary.204.41bd367f728e700d45b989943a044800197ed2b8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"41bd367f728e700d45b989943a044800197ed2b8","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.1--canary.204.41bd367f728e700d45b989943a044800197ed2b8.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-JEHcX/LD/PD3EUrKbgQChJoDLMWybRvgfK2ikdG7d5EjnFTyEBR445XHyFYG4UpHnFs0BJdffANYK5VLBCZDXg==","shasum":"3c6a03f04d017ba414d86322192c4fa3d6bd5651","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.1--canary.204.41bd367f728e700d45b989943a044800197ed2b8.0.tgz","fileCount":80,"unpackedSize":1838692,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHLW7IvtDpmUyXwygCw2uZpBouHfSKEEg6L/TzFAL/9uAiEAht7lKnQTIxz/fPoC07GYr+wMnaaP2ylhkKsaw1T1B/I="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.1--canary.204.41bd367f728e700d45b989943a044800197ed2b8.0_1633095139814_0.8008835832298224"},"_hasShrinkwrap":false},"4.6.1--canary.205.5a23353f3ffe085e45dc376150729d35bd5fea0a.0":{"name":"@sberdevices/assistant-client","version":"4.6.1--canary.205.5a23353f3ffe085e45dc376150729d35bd5fea0a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5a23353f3ffe085e45dc376150729d35bd5fea0a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\nДоступ к API осуществляется через глобальную переменную `assistant`.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.1--canary.205.5a23353f3ffe085e45dc376150729d35bd5fea0a.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-YSKMU/ZBylK4q2mGwEMb4d8pffQlzVOBrbJzquQLryTkB2m8DKFH13Bu5cBSHDqn93T3t7bDrNhobllRhzE4VQ==","shasum":"6d66e919f1002ab0a8ef4b8797361d4ebcf2dbb5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.1--canary.205.5a23353f3ffe085e45dc376150729d35bd5fea0a.0.tgz","fileCount":80,"unpackedSize":1838772,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE4wggZt7ucfIC3gahYdevLnohpLSw+URZyJ5Q+21PerAiAU9x3TYufuZViT+Ou9BY1q4PrJmMD2iUeoxiO6X6eb3w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.1--canary.205.5a23353f3ffe085e45dc376150729d35bd5fea0a.0_1633099827240_0.8568130835087076"},"_hasShrinkwrap":false},"4.7.0--canary.206.d0876e7b78199f7cd5317975b1087739ca509734.0":{"name":"@sberdevices/assistant-client","version":"4.7.0--canary.206.d0876e7b78199f7cd5317975b1087739ca509734.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d0876e7b78199f7cd5317975b1087739ca509734","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.0--canary.206.d0876e7b78199f7cd5317975b1087739ca509734.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-n/tw7NJDq1Em1ZA/dAGNm7wQGfV6cY0vhxgVMt8WyIhGCUbPFsD08IA/n1PqV9jFd10Ydh844j12aE31UQskew==","shasum":"bed73650c844bd6b20e36aee33c00a189221d45e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.0--canary.206.d0876e7b78199f7cd5317975b1087739ca509734.0.tgz","fileCount":96,"unpackedSize":1858787,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPxzIlKm5IamlFuJwFlsU5av2NrvAiH+pChEqZ9lGMjgIgBRB56Htg8LodHN6IwGzC+iqQsBlE2upX32Or0BS5U88="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.0--canary.206.d0876e7b78199f7cd5317975b1087739ca509734.0_1633248045915_0.40688912298601276"},"_hasShrinkwrap":false},"4.7.0--canary.206.705a143b8d70ab82d5d541e0c63383b462b9322d.0":{"name":"@sberdevices/assistant-client","version":"4.7.0--canary.206.705a143b8d70ab82d5d541e0c63383b462b9322d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"705a143b8d70ab82d5d541e0c63383b462b9322d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.0--canary.206.705a143b8d70ab82d5d541e0c63383b462b9322d.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-ub3buzbatXFWUUgnLWGJKyleS9wF2fjA8vtznEGU72JT3TbPv0roFkFurjkCqtSaocXs9O864TP87TmmGnDStw==","shasum":"7b781367b2d711076cb69a3a7f5abe06733bbbfc","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.0--canary.206.705a143b8d70ab82d5d541e0c63383b462b9322d.0.tgz","fileCount":96,"unpackedSize":1858786,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGc04HKwstXKM/4KJoSfJldhF79/hNzLor2CetcBZcO2AiEAj8p9TGN7R4mGTV6GuCWoIVxO8me9PtiLWk261xF8HYE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.0--canary.206.705a143b8d70ab82d5d541e0c63383b462b9322d.0_1633331189428_0.3427155724112967"},"_hasShrinkwrap":false},"4.6.1--canary.205.918ebfb9e3d019a822978bb74beb4aa8f4da501a.0":{"name":"@sberdevices/assistant-client","version":"4.6.1--canary.205.918ebfb9e3d019a822978bb74beb4aa8f4da501a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"918ebfb9e3d019a822978bb74beb4aa8f4da501a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\nДоступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере:\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.6.1/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.6.1/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState, getRecoveryState, });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.1--canary.205.918ebfb9e3d019a822978bb74beb4aa8f4da501a.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-eLRgihVzEm6OZL367hq/ywGqUOrQD6yk9uGji/5jeV9D3mZJ8otuxr7IMZ5gmKZnR5hVZEDDKKJWX1tztvfLPA==","shasum":"718e8b00c961884496a9c9af8f3823cbceb8a749","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.1--canary.205.918ebfb9e3d019a822978bb74beb4aa8f4da501a.0.tgz","fileCount":80,"unpackedSize":1839897,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD9xmncP6XNkwCXyrdKMJ7nYDkQO7oArRiuDuTHQsJbWQIhAIW7r8HyYR4o9Taz/CO0zj6vhfaC2uKdRjznt7uneZE1"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.1--canary.205.918ebfb9e3d019a822978bb74beb4aa8f4da501a.0_1633332066523_0.6805872164171676"},"_hasShrinkwrap":false},"4.7.0--canary.206.c30308795a20682c6fbfd7ae5ff119e4b5d6e43b.0":{"name":"@sberdevices/assistant-client","version":"4.7.0--canary.206.c30308795a20682c6fbfd7ae5ff119e4b5d6e43b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c30308795a20682c6fbfd7ae5ff119e4b5d6e43b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.0--canary.206.c30308795a20682c6fbfd7ae5ff119e4b5d6e43b.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-/5DCn9gVGwDmLHmXaboz0L53s9PUiPv6BbEtppa9XbWX9IndxELc/cx3IhDB72Sjs+ekzRa7dsZzaprYZ2ycxQ==","shasum":"2fa186f6734c2b471766e95a9c920624fb9a90cb","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.0--canary.206.c30308795a20682c6fbfd7ae5ff119e4b5d6e43b.0.tgz","fileCount":98,"unpackedSize":1861932,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIESYgritD3/gxkWs/aGyHhOH66RVLjE7X0VigllkyqHQAiEA7sZjLdbDYpURq9ku/501WEJ6UwAAcS1WcKwtfxFXjU0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.0--canary.206.c30308795a20682c6fbfd7ae5ff119e4b5d6e43b.0_1633349836915_0.27556050997183523"},"_hasShrinkwrap":false},"4.7.0--canary.206.0472eb24dfb84478c9f50a75cfc98305ef20f3c3.0":{"name":"@sberdevices/assistant-client","version":"4.7.0--canary.206.0472eb24dfb84478c9f50a75cfc98305ef20f3c3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0472eb24dfb84478c9f50a75cfc98305ef20f3c3","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.0--canary.206.0472eb24dfb84478c9f50a75cfc98305ef20f3c3.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-/EzmLUKUmy5veavsk3XsqulBzk54z58ffSU3bjcxDgvE6kMsB6P18PpPHF8S2/vGVVYK2KUcXjvP9nZ5OE2P4Q==","shasum":"f852fde0f172d09b46645490ac11ccf88cca7de2","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.0--canary.206.0472eb24dfb84478c9f50a75cfc98305ef20f3c3.0.tgz","fileCount":98,"unpackedSize":1862073,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGPrvzFXGKUDInwu4wzm/L+9/HXRYJX3AxT8VBy0Mvp7AiEAgk1gHaQPjIe6KuRRLJIxVaKy2+NxeShHRSTzVLe6qq4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.0--canary.206.0472eb24dfb84478c9f50a75cfc98305ef20f3c3.0_1633351430287_0.3804821330163808"},"_hasShrinkwrap":false},"4.7.0--canary.206.f6f49b43ef27770b658cc770f302eb433328e057.0":{"name":"@sberdevices/assistant-client","version":"4.7.0--canary.206.f6f49b43ef27770b658cc770f302eb433328e057.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f6f49b43ef27770b658cc770f302eb433328e057","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.0--canary.206.f6f49b43ef27770b658cc770f302eb433328e057.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-fVOHAKNhCwQMyOBT/TDrfZ0j45ayy+6LvXfbvbSofPdEFR8kBAC6gZDgjlzpmKObHzmsCHdkDUCvKrTizx7iEA==","shasum":"672281799e35dddd1a921e58e1556ce7ba32a593","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.0--canary.206.f6f49b43ef27770b658cc770f302eb433328e057.0.tgz","fileCount":98,"unpackedSize":1864543,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD42jeIdZoTDo8oC3MNv+MkesV3UK0YqXEQ34HK0WFCdwIgHcURVksv3TuesVOCo2c5hduNt9TTalxjO0lbmi4Q48A="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.0--canary.206.f6f49b43ef27770b658cc770f302eb433328e057.0_1633354829312_0.03822909038699174"},"_hasShrinkwrap":false},"4.7.0--canary.206.e541c53da8f3b6eabd1daf61b30860c260b5e192.0":{"name":"@sberdevices/assistant-client","version":"4.7.0--canary.206.e541c53da8f3b6eabd1daf61b30860c260b5e192.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e541c53da8f3b6eabd1daf61b30860c260b5e192","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.0--canary.206.e541c53da8f3b6eabd1daf61b30860c260b5e192.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-sqJ6MTcNUTUNu8043uJ0S1S76g+3Yhszbx9CyCWMnYn3jiqrPl0YYWlGKAdA45KqLu7YvOyPHAoiQ6djA3bOrQ==","shasum":"8bea020372babb5f358ee199c155430bde29abaa","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.0--canary.206.e541c53da8f3b6eabd1daf61b30860c260b5e192.0.tgz","fileCount":98,"unpackedSize":1870870,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFcTOPDDAO4e496zx40PnpLzjNaaow6irPKPTFsfZWkLAiB1WmijblaqPYq7Iz48hS4Zl2crhgkFl2n5N3S1de+kLg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.0--canary.206.e541c53da8f3b6eabd1daf61b30860c260b5e192.0_1633357082750_0.9879720897328013"},"_hasShrinkwrap":false},"4.6.1--canary.205.81d2495781acff0ae9388d5f4df4a2860a572315.0":{"name":"@sberdevices/assistant-client","version":"4.6.1--canary.205.81d2495781acff0ae9388d5f4df4a2860a572315.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"81d2495781acff0ae9388d5f4df4a2860a572315","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\nДоступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере:\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.6.1/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.6.1/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState, getRecoveryState, });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.1--canary.205.81d2495781acff0ae9388d5f4df4a2860a572315.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-68ibVBA8/9V7TnW4J9TvTUAxgz7gVdfdaJTJJsF/ZVZq2Ku7KeEJkVnjl3YsVzBxqvUQ9+gbAgGZGNR6YPflFA==","shasum":"202a1400016861d917bbea8f4fb651baafa3f1ed","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.1--canary.205.81d2495781acff0ae9388d5f4df4a2860a572315.0.tgz","fileCount":80,"unpackedSize":1839707,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDa+WA4XWjCu3uQoRY3qXLfl/W0r72KwZFvSFsyzpUzXAiB7wgW2BXfPM4s/EbKrtFEkrbF8SYy/BmNjDYDWCGuEQA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.1--canary.205.81d2495781acff0ae9388d5f4df4a2860a572315.0_1633436435178_0.3653732425175611"},"_hasShrinkwrap":false},"4.6.1--canary.205.6e1333e9d058593ac584d2bc13f8d2ca8b26f674.0":{"name":"@sberdevices/assistant-client","version":"4.6.1--canary.205.6e1333e9d058593ac584d2bc13f8d2ca8b26f674.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6e1333e9d058593ac584d2bc13f8d2ca8b26f674","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае для браузеров обязательно подключение react.\nВерсии react и assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере:\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.6.1/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.6.1/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState, getRecoveryState, });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.1--canary.205.6e1333e9d058593ac584d2bc13f8d2ca8b26f674.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-goylBAGQOdyb4qU5Bkg1E4elaMjCLU6hP9VYndZNT7V1rBacB9jVAs0XqwR7dycYC3WWqVPH5e6jpewEwFmaPg==","shasum":"c3f76cbfd21170b1f43982076ebf5c7dd52c206f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.1--canary.205.6e1333e9d058593ac584d2bc13f8d2ca8b26f674.0.tgz","fileCount":80,"unpackedSize":1839733,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDfFyMmiVvDdbRXbvuaYW+JKccPw/P/mPYMFx88FyzwSQIgY5qjbg7Jr2C5gEDUeZ7CPdJqR1MZXvVWYq1Xhlrtq9w="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.1--canary.205.6e1333e9d058593ac584d2bc13f8d2ca8b26f674.0_1633440722576_0.8081229973056443"},"_hasShrinkwrap":false},"4.6.1--canary.207.e082d127b3bb9b562010f6aeebee9c38975f1e86.0":{"name":"@sberdevices/assistant-client","version":"4.6.1--canary.207.e082d127b3bb9b562010f6aeebee9c38975f1e86.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e082d127b3bb9b562010f6aeebee9c38975f1e86","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.1--canary.207.e082d127b3bb9b562010f6aeebee9c38975f1e86.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-AOlTktaECjno6u17QiOabrtoHzRE5sPS/4RCRzxH8UVefJStXPm3CIYNL+EByMwsBIEAzbGtL8fT1lK2us+PRQ==","shasum":"eb8fd4e358acc7f635f923ccfcd1f0859c8e0bb9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.1--canary.207.e082d127b3bb9b562010f6aeebee9c38975f1e86.0.tgz","fileCount":80,"unpackedSize":1839177,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDWupw8lImCnwfZ8aR5ZBO80PjsBO/4yBrdCPZoyA4u7wIgQ4cz6HEkYOMa+tnreEKMF56id0hsdbzB+BB7EPxlHz8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.1--canary.207.e082d127b3bb9b562010f6aeebee9c38975f1e86.0_1633516391805_0.09183592952646835"},"_hasShrinkwrap":false},"4.6.1--canary.207.5a32d96167f7b9f7986a85ae134120c9ed87c4d9.0":{"name":"@sberdevices/assistant-client","version":"4.6.1--canary.207.5a32d96167f7b9f7986a85ae134120c9ed87c4d9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5a32d96167f7b9f7986a85ae134120c9ed87c4d9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.1--canary.207.5a32d96167f7b9f7986a85ae134120c9ed87c4d9.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-FoVnss9UEn6Mmgno0ZcKoWMptPAxuFx4ati8aMMxty6sjefy8eMsjrG2YL3nJL2tTlkW5bT5zC1CLgeasobZDw==","shasum":"f1620c1fad15973d7aad06755f22a30f0db7a4e7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.1--canary.207.5a32d96167f7b9f7986a85ae134120c9ed87c4d9.0.tgz","fileCount":80,"unpackedSize":1838890,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHGDAA/8TTAugO0NjoJDSAerecjq6YkHMy78E5Wz2NLfAiA/e9wVMrlDh5xkrL0wDkjQLwHSxRr2aU8HhvU1ORFKog=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.1--canary.207.5a32d96167f7b9f7986a85ae134120c9ed87c4d9.0_1633518561512_0.01284906347274184"},"_hasShrinkwrap":false},"4.6.1--canary.205.a90a005023f5073188d0adff2d9abed543ec62a3.0":{"name":"@sberdevices/assistant-client","version":"4.6.1--canary.205.a90a005023f5073188d0adff2d9abed543ec62a3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a90a005023f5073188d0adff2d9abed543ec62a3","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.6.1/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.6.1/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.6.1--canary.205.a90a005023f5073188d0adff2d9abed543ec62a3.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-zo3Hf/o1ci5Py8F3Ut9/AkZYT8Az4OYLO5qER0j87Wi/W8yj38ezfgydlqUefpqaI0nJz7PgZI0h4iieCz3wsQ==","shasum":"0f5d38f7727c87415b4526451adb2b1bcc7ac029","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.6.1--canary.205.a90a005023f5073188d0adff2d9abed543ec62a3.0.tgz","fileCount":80,"unpackedSize":1839747,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHdmMWXbAeyEcryCgspGG4mOp80zyYj6gyIdBZiHClCtAiBgjplYWyx7kLCsvtZWUQ+cWYbtD8s97f15rasVOd+yog=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.6.1--canary.205.a90a005023f5073188d0adff2d9abed543ec62a3.0_1633518812012_0.7093987158404746"},"_hasShrinkwrap":false},"4.7.0--canary.207.82690778300022c0cdb676df11b1d1da6d1df74b.0":{"name":"@sberdevices/assistant-client","version":"4.7.0--canary.207.82690778300022c0cdb676df11b1d1da6d1df74b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"82690778300022c0cdb676df11b1d1da6d1df74b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n#### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`. В этом случае обязательно подключение react. Версии react и assistant сlient можно поменять в src.\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@2.1.0/umd/assistant.min.js\"></script>\n```\n\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.0--canary.207.82690778300022c0cdb676df11b1d1da6d1df74b.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-/Ic8CEK5GDUXabR4kW4kcoZbZJELnTRCZb735CJpHfNFsYP5JAO+6hZsvwI6A3fiHX7NepousmBZGYPDlHSwiQ==","shasum":"630d50fcbdae325699e0d96d56305b1e1105d847","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.0--canary.207.82690778300022c0cdb676df11b1d1da6d1df74b.0.tgz","fileCount":80,"unpackedSize":1838890,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQConiL6BUaWHOTVY6VL49+jgOh/vPR4Q+l5M3hqLarlIAIgQW+XewvDdbxbnWKamKneBxZRAvfjclSajVgAsa0LoZA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.0--canary.207.82690778300022c0cdb676df11b1d1da6d1df74b.0_1633520951719_0.37756829432052075"},"_hasShrinkwrap":false},"4.7.0":{"name":"@sberdevices/assistant-client","version":"4.7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"78a16a55f5e2b8f10c009dcb3e539870ceb590ef","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-70ftqjeHEzg/AVBoW/zeCgwt/RywjYa1M3e56LxiAzR7Xf6eQYMCIv8+S2KpnphxBr5K3vFdjs50sKfDAKP3Hg==","shasum":"a49eb32a3f282b2ce66292df6174b4151c2811a5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.0.tgz","fileCount":80,"unpackedSize":1840598,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD0h5xonBGWVY6c0nq412pghlkCx/GVYUicZJrrfbXUzQIgJwDkpOsvxkg8hG7tgKnrhyLvw+fCVQbto+VvlQoavI0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.0_1633521396866_0.7484728425724316"},"_hasShrinkwrap":false},"4.7.1--canary.208.163760deba52855c338f91126fda1ad1254f8d1c.0":{"name":"@sberdevices/assistant-client","version":"4.7.1--canary.208.163760deba52855c338f91126fda1ad1254f8d1c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"163760deba52855c338f91126fda1ad1254f8d1c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.1--canary.208.163760deba52855c338f91126fda1ad1254f8d1c.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-WM2IGS2V0EEuH7EFZ4lrAOJ18yu/20FA/IYhW+XtX3HN7IUSkKsqeE5o6m5cCzw1jBTf1VU3zP91dWQs5QkPUw==","shasum":"20228510042c7ca187aa49c65b72fff67f98b8e1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.1--canary.208.163760deba52855c338f91126fda1ad1254f8d1c.0.tgz","fileCount":80,"unpackedSize":1840818,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIClThDHt3Jdd2GJn4j9WYpAfn8uIysDvRGAXJNo8ShNuAiARQYlcNtNTErtMXZnVBw0K4i+9QEN1dnBWapbREb3++Q=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.1--canary.208.163760deba52855c338f91126fda1ad1254f8d1c.0_1633521737727_0.7292407092224271"},"_hasShrinkwrap":false},"4.7.1--canary.210.9250d17451adb9dc7e7b53d5f99911795bd410ae.0":{"name":"@sberdevices/assistant-client","version":"4.7.1--canary.210.9250d17451adb9dc7e7b53d5f99911795bd410ae.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/types":"0.6.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9250d17451adb9dc7e7b53d5f99911795bd410ae","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.1--canary.210.9250d17451adb9dc7e7b53d5f99911795bd410ae.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-7Zpw/0HQY5BLI1sApLuDR1L5MlOIs22izpnIBnwIyiz+vbqXy5vVa3z6dCCGOOTD1xIXKSeYhLHoefIvabXYEQ==","shasum":"26f99755e9e4903304c875b49e3a9d23f92d273a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.1--canary.210.9250d17451adb9dc7e7b53d5f99911795bd410ae.0.tgz","fileCount":80,"unpackedSize":1841347,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA1B8rn5ZctEsYmeyiDArakXqm0aPI8PTa283+9geyuOAiEA/lH+q32zvsTdImbuWx+e7U345jAthTEGz4eHwm3odII="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.1--canary.210.9250d17451adb9dc7e7b53d5f99911795bd410ae.0_1633936787885_0.026746146109219104"},"_hasShrinkwrap":false},"4.7.1--canary.211.b2ea6c7e024ed5ce743d5408e216bfa92f19f0ec.0":{"name":"@sberdevices/assistant-client","version":"4.7.1--canary.211.b2ea6c7e024ed5ce743d5408e216bfa92f19f0ec.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b2ea6c7e024ed5ce743d5408e216bfa92f19f0ec","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.7.1--canary.211.b2ea6c7e024ed5ce743d5408e216bfa92f19f0ec.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-1Wxer4Le2gn27sYobSYNmYPe9WV1k8XEClgrWP1/36NeleYkz0kc5QVdDYErK++X44LOW9a2xECKKxSOOncXZA==","shasum":"a4df98d6bd8a5b7b8573e417e2c06f0f326b41cf","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.7.1--canary.211.b2ea6c7e024ed5ce743d5408e216bfa92f19f0ec.0.tgz","fileCount":80,"unpackedSize":1840591,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDp7mceVVDNQ2vE7J915WjedXDNg2twPvAEGNK5kDLAPwIhANI0fVuRjT/AVAFgxlpHVWFig+z77lsw4IGCHG9GcAtD"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.7.1--canary.211.b2ea6c7e024ed5ce743d5408e216bfa92f19f0ec.0_1633937098156_0.761932333834346"},"_hasShrinkwrap":false},"4.8.0":{"name":"@sberdevices/assistant-client","version":"4.8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1d708b893d74651b0a632d5170c24b34795e7163","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.8.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-ifl6J8ja+Namt8ueT960LEA0ovwRLyzWrOOSp2T8Mcgvv7UyUCLrK1rozpUgOp8zGgkvK2Djh9W5ZYMwtJ2Z+g==","shasum":"0ee3b3148f68d43d164e937d38d7b4b710b9caf2","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.8.0.tgz","fileCount":80,"unpackedSize":1842305,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpC+uZONMwOkmQKVGs4+zAmQLorYE60h9xE7+xTOkRlAIgDEH6V5B7U5CHadnyFgyAI1OI4MMzk/mIUNFPTrShQQw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.8.0_1633944327435_0.7776089596538445"},"_hasShrinkwrap":false},"4.8.1":{"name":"@sberdevices/assistant-client","version":"4.8.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"83bcc34e5a1f04583bb474d07e6b647392896582","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.8.1","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-dXStMyErGyQUKWg6wlNPhDEEWmNyb/ZYN0IQgMOiFst9qhaAJDcXXdnOrdpRCxrpsQ/t6zCYN5M8i8OVyIBgwA==","shasum":"baa1d5338ebe1842648d1182da3ffd668a702fa3","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.8.1.tgz","fileCount":82,"unpackedSize":1846714,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBykHA3dFY5ZhXbE2XSYlnjTzmgMWju1lFUAnpckYeYVAiEAkFD8zFf4ZYPB2dSEGhqIee4+3/vy/I28vTVW8eIkrFY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.8.1_1634024310688_0.1038000926417122"},"_hasShrinkwrap":false},"4.8.2--canary.212.796835ea7f38be55cfa2c56901fa0eebfc46cf5c.0":{"name":"@sberdevices/assistant-client","version":"4.8.2--canary.212.796835ea7f38be55cfa2c56901fa0eebfc46cf5c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"796835ea7f38be55cfa2c56901fa0eebfc46cf5c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.8.2--canary.212.796835ea7f38be55cfa2c56901fa0eebfc46cf5c.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-DDImi+O/rgqutVdun0jkJkLigMBBvZQobUZ5WBzPHiecFMoqBtlK812HjUV+qsAkQQJ57BfupsThh/twRIHL9Q==","shasum":"d22cc42218d13986693e2ca8c3e2b779e313c67b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.8.2--canary.212.796835ea7f38be55cfa2c56901fa0eebfc46cf5c.0.tgz","fileCount":82,"unpackedSize":1846934,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDqHf8GH2F3aDvCFJQX5N4lfbprtAaq/B54wBdjoCT3LAiAoZGFzKuB/R6xWtzTL8Krw2qAhUhxw2FA9MNXcu47cEw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.8.2--canary.212.796835ea7f38be55cfa2c56901fa0eebfc46cf5c.0_1634030800695_0.9136023055216853"},"_hasShrinkwrap":false},"4.9.0--canary.202.e1e53a4cd90d8edc943f3715c98a065da909022d.0":{"name":"@sberdevices/assistant-client","version":"4.9.0--canary.202.e1e53a4cd90d8edc943f3715c98a065da909022d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e1e53a4cd90d8edc943f3715c98a065da909022d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.0--canary.202.e1e53a4cd90d8edc943f3715c98a065da909022d.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-0BlSHIwCW1dy0BmwsfFWjb8aRo4f2VvTVy1RkgdhLwlYtFoGRaIfiVvpG0ooi0e7b7YQaeSfZsBKEYgVR/blhg==","shasum":"a2a5c8b7a69dc48f16b93d6f3a6ae372ca5ab1ab","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.0--canary.202.e1e53a4cd90d8edc943f3715c98a065da909022d.0.tgz","fileCount":82,"unpackedSize":1847821,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFmRH7f7AZ8JEk1DUJcm1YaCdflzNZcU0QRYFSYgtHcZAiB+peitYXfBic2yTzWMp7K8pfXgisN4krCZNo8Q+VHFLw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.0--canary.202.e1e53a4cd90d8edc943f3715c98a065da909022d.0_1634034575433_0.45268762047872757"},"_hasShrinkwrap":false},"4.9.0":{"name":"@sberdevices/assistant-client","version":"4.9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c56e535142ca954b69936749c7b09db35815f025","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-D1htZOoNtuxaKDg4qDrpR63qTwGzLqXIrE7AF1C7fa4RvJD/iIvUL+iZ6bV5ki416r5LhwV61yJbT2EcAixP/w==","shasum":"6397ad2e99fcb43c675890e143bd5762006bc34d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.0.tgz","fileCount":82,"unpackedSize":1848201,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCR7hWhnNavoU8ffE9vOUWdpr/XD++gy3f77yqvl+wk3QIhAImNNSo0pMxlBiqCMdcAkksEuwf96Qo4ZHsFL7JB4GWZ"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.0_1634035081516_0.041517396709552346"},"_hasShrinkwrap":false},"4.10.0--canary.216.a08c891c30d2cd28d69c9d8b69b82c26de123396.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.216.a08c891c30d2cd28d69c9d8b69b82c26de123396.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a08c891c30d2cd28d69c9d8b69b82c26de123396","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.216.a08c891c30d2cd28d69c9d8b69b82c26de123396.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-MMnTjflG79zfy+JU42H3emdbZlfUg2y94JcQ7DLjqj3AQZQvJC91XQhVm12Ow4vSTY9uTpwlvxljX18Aar4zQg==","shasum":"82c73d3d9aece40c055710a3b5f7be234e5989c3","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.216.a08c891c30d2cd28d69c9d8b69b82c26de123396.0.tgz","fileCount":82,"unpackedSize":1850059,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDgzHE79Mt19ESqBKIXQRMhg7QvmwE20VvM+V4jLUgsSAiEAkNuOjVhkKQP1iiigpnKPCSx+IXhWedhEIgS4ehU5wh8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.216.a08c891c30d2cd28d69c9d8b69b82c26de123396.0_1634723769534_0.8746352572049332"},"_hasShrinkwrap":false},"4.10.0--canary.217.d1ac12c011343d9a67483738950dd97f0200a7ac.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.217.d1ac12c011343d9a67483738950dd97f0200a7ac.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d1ac12c011343d9a67483738950dd97f0200a7ac","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.217.d1ac12c011343d9a67483738950dd97f0200a7ac.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-6RbqG5OJiAJGbkps7sIF58MaYouZvPkq2HFcgCvktG3GA8UwL7tTrtxqTO4ha4F7V8cQ7fSdxKowk5+Vm5Njfw==","shasum":"412152c9ab7fa96468481d2e9e7e7d098ee2740c","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.217.d1ac12c011343d9a67483738950dd97f0200a7ac.0.tgz","fileCount":82,"unpackedSize":1849488,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFtLWbK6mWJWlcuMUkvNNhW4R7i7NjB76h7GjkFaG2V3AiEA3yGomEJBifHwArMpXqH2vsC0BmHUUAEstBtLXLzMPB0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.217.d1ac12c011343d9a67483738950dd97f0200a7ac.0_1634742203275_0.922741972634135"},"_hasShrinkwrap":false},"4.10.0--canary.206.f6ce665e94aab117ee01c4f139f96f0d5bff6480.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.206.f6ce665e94aab117ee01c4f139f96f0d5bff6480.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f6ce665e94aab117ee01c4f139f96f0d5bff6480","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.206.f6ce665e94aab117ee01c4f139f96f0d5bff6480.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-u00rUebZSjSnFAGWnuaUPwun0wl/aj1XvZePxWRe21RVtXh0seSjYx4Gh3QQPDvhgOXCR3RjHyG6WZdIDj7oGg==","shasum":"6773077e09dc7f08a336370bbc3a40d07afb1414","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.206.f6ce665e94aab117ee01c4f139f96f0d5bff6480.0.tgz","fileCount":102,"unpackedSize":1885533,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDhBZFO1mh6fdHE3GxLMNQX6alj956/ytf+1Td+15EbdAiAGbyxMG2vfOO8WbXjUuh6YHDVHPLhQN12S8A09flAlpg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.206.f6ce665e94aab117ee01c4f139f96f0d5bff6480.0_1634757326836_0.33590650148047607"},"_hasShrinkwrap":false},"4.10.0--canary.206.260462766c65a22f0ea012926a2c4e1ffb1eed87.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.206.260462766c65a22f0ea012926a2c4e1ffb1eed87.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"260462766c65a22f0ea012926a2c4e1ffb1eed87","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.206.260462766c65a22f0ea012926a2c4e1ffb1eed87.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-thVCIMYyDkrxW+Tc4SGVDnbxqmnTDljO4pX9p3obJfwW2ucvnXUEi5GVo5MHGPX6v3Dw2XdSJi/eoiGHk4B9Dw==","shasum":"e4f6a8407b46c6821b22ea9f943925c7f6cc5004","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.206.260462766c65a22f0ea012926a2c4e1ffb1eed87.0.tgz","fileCount":100,"unpackedSize":1885058,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAjFyf4rpnFWr1BDo0o2oAYjCckdOtXnUN4mpXMvGDiqAiEAyl3D4Q4pjWUjNd1CXuoLRfVpIUyQEwKI8qcVQ7CM9YY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.206.260462766c65a22f0ea012926a2c4e1ffb1eed87.0_1634757799663_0.64735838929709"},"_hasShrinkwrap":false},"4.10.0--canary.206.4290ab417b696464b9c8bdc2981d42091e825084.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.206.4290ab417b696464b9c8bdc2981d42091e825084.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4290ab417b696464b9c8bdc2981d42091e825084","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.206.4290ab417b696464b9c8bdc2981d42091e825084.0","_nodeVersion":"12.22.6","_npmVersion":"6.14.15","dist":{"integrity":"sha512-v9bTI1zYKjkaUzjvSjxMO1vVNRMEBEDADGS6Zxca3mbiA88aDlXFlpgoBi4UMZ0xXbqHpgS2wEEkkB1T5NTJdQ==","shasum":"3b61bd19576504f1b1a0fc516dad6fe4297a91d4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.206.4290ab417b696464b9c8bdc2981d42091e825084.0.tgz","fileCount":100,"unpackedSize":1885150,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBiEUlTxTA56DnRi43hm+VOHigMkyMDMsFG7nyH2zkADAiBgAE6iZWGqtDVkogmcQPuWz05fy82q4vOVT7tF9k5amQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.206.4290ab417b696464b9c8bdc2981d42091e825084.0_1634758414353_0.7000913822459853"},"_hasShrinkwrap":false},"4.10.0--canary.206.05e7a08a1e239cb42c800205495d59400ca3d6d3.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.206.05e7a08a1e239cb42c800205495d59400ca3d6d3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"05e7a08a1e239cb42c800205495d59400ca3d6d3","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.206.05e7a08a1e239cb42c800205495d59400ca3d6d3.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-N8P7k3NJAr1z+Chqnng04TVCdW6x9iwAgGpPj664orqAz1EfpjXT/LLV8lUY9OVQlMMIAPCqIDJQBiUiI2a4lQ==","shasum":"268c139971e2972d7c8fe3462aab5a18b40f2238","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.206.05e7a08a1e239cb42c800205495d59400ca3d6d3.0.tgz","fileCount":100,"unpackedSize":1885015,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAfhWe1LuKO1A2ceWOxmKb79HercTt220gTZ3asHqRTbAiAlPNFw8RLXBlwTLW0iymZdZgSNRvwQQA326st2mF0cOg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.206.05e7a08a1e239cb42c800205495d59400ca3d6d3.0_1634907506816_0.5806474028546318"},"_hasShrinkwrap":false},"4.10.0--canary.206.fe1c6fab93d7046b07e8f154b2182a9a62487202.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.206.fe1c6fab93d7046b07e8f154b2182a9a62487202.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"fe1c6fab93d7046b07e8f154b2182a9a62487202","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.206.fe1c6fab93d7046b07e8f154b2182a9a62487202.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Qdp8yck+lVR+RuCXjr7S6Ztl7feajBjLqq/OtBxWgmUSsfcj7I/TWUOzV+8sqpmncnopNCBew6joYkueTxk9cQ==","shasum":"b8494f237d8c627b7d99549c583261480c730c81","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.206.fe1c6fab93d7046b07e8f154b2182a9a62487202.0.tgz","fileCount":100,"unpackedSize":1885845,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICvmAcc1oWKoc9xQ6qQV6UggxylKGijdKUt0BOJtpdtNAiEAl9ajBWvlfwvPX0eDER0uwlkR6E5I/+P+C4yU4tgHt0g="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.206.fe1c6fab93d7046b07e8f154b2182a9a62487202.0_1634910284616_0.30029656264183924"},"_hasShrinkwrap":false},"4.9.1--canary.222.b3dfbc353478f6f78b00490cfd9970e1ade05335.0":{"name":"@sberdevices/assistant-client","version":"4.9.1--canary.222.b3dfbc353478f6f78b00490cfd9970e1ade05335.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b3dfbc353478f6f78b00490cfd9970e1ade05335","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.1--canary.222.b3dfbc353478f6f78b00490cfd9970e1ade05335.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-4nv8eRiaeBjIiMtGoCitDttsPLnn+Pb6yYuTJ8dsvT3zxhh9LNSQ240zaPMGqvxnRU+aGTjxww93r7ALiHOjXw==","shasum":"55240999ba5131dab0d943ae8dc6e8c5d590370a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.1--canary.222.b3dfbc353478f6f78b00490cfd9970e1ade05335.0.tgz","fileCount":82,"unpackedSize":1848675,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFvFKx/xMlT73nAqI5+5sehscwOXK50tx0bOihSVy4CHAiBMfFHPWowrPOG5cXOG/kqqMXWd/w5Nj14l5vatTflZow=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.1--canary.222.b3dfbc353478f6f78b00490cfd9970e1ade05335.0_1635255048325_0.09243642315015488"},"_hasShrinkwrap":false},"4.9.1--canary.222.0f487b33bba4096191af3e481c7bb5f640254012.0":{"name":"@sberdevices/assistant-client","version":"4.9.1--canary.222.0f487b33bba4096191af3e481c7bb5f640254012.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0f487b33bba4096191af3e481c7bb5f640254012","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.1--canary.222.0f487b33bba4096191af3e481c7bb5f640254012.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-ohZ1wT0+x3DekQugRNTX0a/kUhxp7+52EIO6/axxXr27oWiS/pitE+vhh/kxp2XQp3eeEm3BwHilDb8w7qMr9Q==","shasum":"a05ee6d2cdba6ee85052750b5ea1fdbb0cde5e8f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.1--canary.222.0f487b33bba4096191af3e481c7bb5f640254012.0.tgz","fileCount":82,"unpackedSize":1848572,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCKzZ5raoqbX4MDUaZ7HqMeEIXj4F3M9aCOPk7heyORfAIhAIHkaVJF6LdvZ5fPQNR0wFPWGpqaetOfbwl8gYc85KgP"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.1--canary.222.0f487b33bba4096191af3e481c7bb5f640254012.0_1635257001524_0.3299661563500984"},"_hasShrinkwrap":false},"4.10.0--canary.217.b977079c87795d648bd138aabf3191d95fa88d17.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.217.b977079c87795d648bd138aabf3191d95fa88d17.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b977079c87795d648bd138aabf3191d95fa88d17","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.217.b977079c87795d648bd138aabf3191d95fa88d17.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-rNjRue39UchkxUhUHwa8zuZ/g31QtA/9ST+5qOTaLyizUgZUpWnvaqpqwfIaZaAU9QEEftPTF/HYaUufeIsdCw==","shasum":"7914673536c11e5cd98a8635093e038ceb17cbe1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.217.b977079c87795d648bd138aabf3191d95fa88d17.0.tgz","fileCount":80,"unpackedSize":1844511,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCBHMYDge/x8vj+koa/C7g52a8yeEvcI1YvDlTRJp9GIQIgBvliL3zpy+zm79N315CvRfKfKkdNKBcaKRL7ktO26A8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.217.b977079c87795d648bd138aabf3191d95fa88d17.0_1635257451848_0.06972773641771002"},"_hasShrinkwrap":false},"4.9.1":{"name":"@sberdevices/assistant-client","version":"4.9.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b5f10fd3ff2ebb3b3199c07939c6338cd5aa306d","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.1","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-OfB1OGa3XUtdtEQO0wK8wiQLh9kxRDwuRlpN2bKu1NI65+z5iMHFrc8OA4OQKW/xdq9PPEqzUPtoLRaWgraVYQ==","shasum":"ca83544d08cc772fba552014e09d59acb7978283","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.1.tgz","fileCount":82,"unpackedSize":1848673,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEWh3mTol7E0LxDX31iLjfac17+Pl0WE7+AX4aehmeebAiANNAFx12U7/ZszlGpUEHdbneUURyGTjmmrO7luSbsAFQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.1_1635310738042_0.6323874134877192"},"_hasShrinkwrap":false},"4.9.2--canary.224.f671bccecff603e9cdcc4b8a7df83ba02d6d8b2f.0":{"name":"@sberdevices/assistant-client","version":"4.9.2--canary.224.f671bccecff603e9cdcc4b8a7df83ba02d6d8b2f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/sdk/voice/asr/index.proto > src/sdk/voice/asr/index.js && pbts src/sdk/voice/asr/index.js -o src/sdk/voice/asr/index.d.ts","mtt":"pbjs -t static-module src/sdk/voice/mtt/index.proto > src/sdk/voice/mtt/index.js && pbts src/sdk/voice/mtt/index.js -o src/sdk/voice/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f671bccecff603e9cdcc4b8a7df83ba02d6d8b2f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.2--canary.224.f671bccecff603e9cdcc4b8a7df83ba02d6d8b2f.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-zKUKZo7IKqyLjydpcZQyyD9HGcnQEH1mm8pmmGyQREcdgAkMdd9/MtQ9XT/AkvkYKleSSb9XCmkGLD+NjkSMtw==","shasum":"bb67b5a9babd035971c8bccb5d79745fbdc35e12","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.2--canary.224.f671bccecff603e9cdcc4b8a7df83ba02d6d8b2f.0.tgz","fileCount":82,"unpackedSize":1849915,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUGUogQMkFV3et359i9N8+F72ir9zblxZB6iG9DYx+awIgCyMYeQARDyqMiaBWfqpG2R0XKKUJPe2G2s2em50/m3g="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.2--canary.224.f671bccecff603e9cdcc4b8a7df83ba02d6d8b2f.0_1635340834654_0.11277303717626741"},"_hasShrinkwrap":false},"4.9.2--canary.226.84efacaada01b74d923de2ae8af5dec21b5ed849.0":{"name":"@sberdevices/assistant-client","version":"4.9.2--canary.226.84efacaada01b74d923de2ae8af5dec21b5ed849.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"84efacaada01b74d923de2ae8af5dec21b5ed849","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.2--canary.226.84efacaada01b74d923de2ae8af5dec21b5ed849.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-nuMFM9WoQYjIKva4Uu6H0RORIE5xR4FXoVx8Yb7FnogGOAaGfkdJfZWqYxgiTyvd3VGJEy6hDxnaSSijU/AbxQ==","shasum":"025617dc25dc6a6e17f226a0ca4ba5f443baadc5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.2--canary.226.84efacaada01b74d923de2ae8af5dec21b5ed849.0.tgz","fileCount":82,"unpackedSize":1849335,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFhzLeTHXGKhPaAUeKg9W17mz1JRfe1g2ZXHbGhpOyUDAiEAuttYOAUmRNJLbKSzcJlvj3VvcnO1ivj9jSXu2xb8UcQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.2--canary.226.84efacaada01b74d923de2ae8af5dec21b5ed849.0_1635495958768_0.18923829547670357"},"_hasShrinkwrap":false},"4.9.2--canary.226.3f1a618ea2be182609a728c992a905b8553a7bff.0":{"name":"@sberdevices/assistant-client","version":"4.9.2--canary.226.3f1a618ea2be182609a728c992a905b8553a7bff.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3f1a618ea2be182609a728c992a905b8553a7bff","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.2--canary.226.3f1a618ea2be182609a728c992a905b8553a7bff.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-GUl49eKbaQYbzbfH+qlq2Zdz6q3nzhK4CoBo33jIoXu4cO2asqyWRdIlv5hFwHMeiQCwC4aT17kQ2Iw1V2aMyw==","shasum":"10c76cf4b59f5990293b7eee2618418b691c85bf","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.2--canary.226.3f1a618ea2be182609a728c992a905b8553a7bff.0.tgz","fileCount":82,"unpackedSize":1849335,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHtMN58hCmCF1DA9Fulr4qfCQzsXu11DIjDexVGoAinuAiEA/U8kRh4hTM06zyzt37I4xcRi+jMQ/1PwTLUO6GZZHd4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.2--canary.226.3f1a618ea2be182609a728c992a905b8553a7bff.0_1635498829066_0.5977165221451439"},"_hasShrinkwrap":false},"4.9.2--canary.224.c62514bbeb9dc17a4a4e5908e41921379ce81d7e.0":{"name":"@sberdevices/assistant-client","version":"4.9.2--canary.224.c62514bbeb9dc17a4a4e5908e41921379ce81d7e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/sdk/voice/recognizers/asr/index.proto > src/sdk/voice/recognizers/asr/index.js && pbts src/sdk/voice/recognizers/asr/index.js -o src/sdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/sdk/voice/recognizers/mtt/index.proto > src/sdk/voice/recognizers/mtt/index.js && pbts src/sdk/voice/recognizers/mtt/index.js -o src/sdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c62514bbeb9dc17a4a4e5908e41921379ce81d7e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.2--canary.224.c62514bbeb9dc17a4a4e5908e41921379ce81d7e.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-3SNvYaTUU/n43QdtXgDl/U4UjTXMyUp7Z2YzIrCB3HNIpo8xQDjrfYeHskZZZt8LAVQdV4LjMfp0AzhCreKfjA==","shasum":"ad54f39e68fab8ab27520503892d68fdd9041a46","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.2--canary.224.c62514bbeb9dc17a4a4e5908e41921379ce81d7e.0.tgz","fileCount":86,"unpackedSize":1860956,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGGBy8N010OX3IhsFzRvedPjLgzmktYaPdzrws3+rjt2AiEAk7tVf2MKflFR4n7I98++V+k6XVPh6aN68X5/xRrHaLs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.2--canary.224.c62514bbeb9dc17a4a4e5908e41921379ce81d7e.0_1635509046947_0.3746232002987522"},"_hasShrinkwrap":false},"4.9.2--canary.226.d3dd44f0598fdae10d3381cc54a461d2af2c46e4.0":{"name":"@sberdevices/assistant-client","version":"4.9.2--canary.226.d3dd44f0598fdae10d3381cc54a461d2af2c46e4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d3dd44f0598fdae10d3381cc54a461d2af2c46e4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.2--canary.226.d3dd44f0598fdae10d3381cc54a461d2af2c46e4.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-X1+NwrnRPl0Z4Zuq2/rjLpvX8Es3mz5XHIMV/vDgmFH0Sz9do7fwsOiRH4Ig6UaMkJeCME7BaOcJtu9Uuz6SuA==","shasum":"695d0c9a687e792012648bd8e17aae1666a58125","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.2--canary.226.d3dd44f0598fdae10d3381cc54a461d2af2c46e4.0.tgz","fileCount":82,"unpackedSize":1849959,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCWzxC+bTQrFtH4cvvTAAxmps7Qb5AgfiVuLTq7ZXA2ogIhAMxe1ML3OkFeV/R3jBSEXCVLyV6j+qZP2n9YvWMSSB4F"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.2--canary.226.d3dd44f0598fdae10d3381cc54a461d2af2c46e4.0_1635782375626_0.5944696041204414"},"_hasShrinkwrap":false},"4.9.2--canary.226.3b1982c6b259075dfb9f7b09528f0d64fa5ca7af.0":{"name":"@sberdevices/assistant-client","version":"4.9.2--canary.226.3b1982c6b259075dfb9f7b09528f0d64fa5ca7af.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3b1982c6b259075dfb9f7b09528f0d64fa5ca7af","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.2--canary.226.3b1982c6b259075dfb9f7b09528f0d64fa5ca7af.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-IuGC2DpfTynMoaXUwjakLTvgudXOOS8N/8O6RAO0o0TqY+swcaz1fh9NNiXabEihtWsQjKwoadoC8gJ6qwnkwg==","shasum":"52f41916981ad9dda5b4f05dcf38b8266d216e91","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.2--canary.226.3b1982c6b259075dfb9f7b09528f0d64fa5ca7af.0.tgz","fileCount":82,"unpackedSize":1849959,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYvmbI5J/dZHS0QXs7SSqqi4eGewBa05mstQmmkspsFAIgUbANt0l6pCoKrYtM9QF84ldq28U7bGM6YSfh1DSkGkw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.2--canary.226.3b1982c6b259075dfb9f7b09528f0d64fa5ca7af.0_1635782602563_0.1415235534140269"},"_hasShrinkwrap":false},"4.9.2--canary.224.ca2e3a9eac3852e15c64840f399cec9d8c7f87e2.0":{"name":"@sberdevices/assistant-client","version":"4.9.2--canary.224.ca2e3a9eac3852e15c64840f399cec9d8c7f87e2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ca2e3a9eac3852e15c64840f399cec9d8c7f87e2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.2--canary.224.ca2e3a9eac3852e15c64840f399cec9d8c7f87e2.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-5d+0ri5QMIU07I9mLItHuFGINfZwQko7FqgVj4fB+r4jf2/wWBt5bFi8uTSE8siFjTP3TDu6KEfjMP3DahuCCw==","shasum":"e740f6a84c460ccead2cce3aeac98bac1646f095","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.2--canary.224.ca2e3a9eac3852e15c64840f399cec9d8c7f87e2.0.tgz","fileCount":86,"unpackedSize":1861190,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcuo5sYAkqf8bzregDPIubmuSRv3sIx+1Tkh/KkJUewQIhALuvobqsPT7JfBzqO1nxzl82wpSQqFEamXwdxMTMhtiN"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.2--canary.224.ca2e3a9eac3852e15c64840f399cec9d8c7f87e2.0_1635858899486_0.9551436789388508"},"_hasShrinkwrap":false},"4.10.0--canary.228.422b880d8b89cae6b302862e844b66f6f89cdb0a.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.228.422b880d8b89cae6b302862e844b66f6f89cdb0a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"422b880d8b89cae6b302862e844b66f6f89cdb0a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.228.422b880d8b89cae6b302862e844b66f6f89cdb0a.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-M9aRrZMsMLG8jY8qzyiHVCTBSlw7roYXAUmw1MQQFsAiBsQOGCHL4EKdoWYA3lnRINFnrLp8qkA0RBKJM3BIFw==","shasum":"359a4408740460905819b337fd0dde3ca29fb3ff","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.228.422b880d8b89cae6b302862e844b66f6f89cdb0a.0.tgz","fileCount":82,"unpackedSize":1849509,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwsdQK+8gucfd12XGm2/wtbY4f9hDmxkKI93OmmJ21nwIhAPFGLCoLLpamzK6yL/Z3aQDgbrPg4r+RWJIpMgPz8mZL"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.228.422b880d8b89cae6b302862e844b66f6f89cdb0a.0_1636381906736_0.9004977762666309"},"_hasShrinkwrap":false},"4.10.0--canary.228.4b6c5c9baa715de142aea8ffb0d1e7cb3b358ae6.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.228.4b6c5c9baa715de142aea8ffb0d1e7cb3b358ae6.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4b6c5c9baa715de142aea8ffb0d1e7cb3b358ae6","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.228.4b6c5c9baa715de142aea8ffb0d1e7cb3b358ae6.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Nd2rgxVI6vWhhgJ4POoFJbhpGCEnhdExGnK7DFw5xiUkyxhVzZoNOV5GeD3gjHxnLlcq/+/2OCszcYVK0940qQ==","shasum":"80a20c3c00c370c4989300a943acfd942e05a215","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.228.4b6c5c9baa715de142aea8ffb0d1e7cb3b358ae6.0.tgz","fileCount":82,"unpackedSize":1849439,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDNRQhtYIPezSkXGkW8kXhduG5b8sv0U5LGIqijGWxQ7AiEAiB/OL/Bh/W7PWZuKlvSec+SRFJjVnYu5PKNRhHix3dw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.228.4b6c5c9baa715de142aea8ffb0d1e7cb3b358ae6.0_1636382053354_0.3926180535872086"},"_hasShrinkwrap":false},"4.9.2--canary.226.002a2d544402d6ba3c5f2c5fd9fae45882061e0a.0":{"name":"@sberdevices/assistant-client","version":"4.9.2--canary.226.002a2d544402d6ba3c5f2c5fd9fae45882061e0a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"002a2d544402d6ba3c5f2c5fd9fae45882061e0a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.2--canary.226.002a2d544402d6ba3c5f2c5fd9fae45882061e0a.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-7g+dH9kNm6GKnRG1x6DntFTOW4kqaS1tyRzqYf0C0F+LZRl1Bj1pGIzl7ydXWOTzE+2GFcxZ7tjiizEU12noLw==","shasum":"e84208fc4bc7f7fa0e626a440f3ed2f39a67bb19","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.2--canary.226.002a2d544402d6ba3c5f2c5fd9fae45882061e0a.0.tgz","fileCount":82,"unpackedSize":1849282,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDuHj2u/pQMlcBzFX0r2L/kuM6GWpqsl1czU8tFEOgelgIgJZp7IUo5s4zlwmmdupPTveJnktUQhsVIRYrdpKfWZfA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.2--canary.226.002a2d544402d6ba3c5f2c5fd9fae45882061e0a.0_1636437919543_0.6894243941866407"},"_hasShrinkwrap":false},"4.9.2--canary.226.2d49921cd786c5e5606c4d7e5129cac744d58d0f.0":{"name":"@sberdevices/assistant-client","version":"4.9.2--canary.226.2d49921cd786c5e5606c4d7e5129cac744d58d0f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2d49921cd786c5e5606c4d7e5129cac744d58d0f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.9.2--canary.226.2d49921cd786c5e5606c4d7e5129cac744d58d0f.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-cZD+JGcXAnIbkL2uqzpqRp2TVh2u9iy3WXYS/AUVmrJ7UqXgYpiQGMmFK8rCgMvYak915tPQtzYIIh8yhhEyVg==","shasum":"54120c468f5d7c2987a8e2cdb96debf52c5d014d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.9.2--canary.226.2d49921cd786c5e5606c4d7e5129cac744d58d0f.0.tgz","fileCount":82,"unpackedSize":1849422,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC9EOeBjpkcrDIIbsA4HBN7IWsFN77+3JEpiAinKbOA0QIgE+RXxGtTZYh6bdeCPFTThI6EtBsdwASuenxwN0Lesrc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.9.2--canary.226.2d49921cd786c5e5606c4d7e5129cac744d58d0f.0_1636438173330_0.7416098760642884"},"_hasShrinkwrap":false},"4.10.0--canary.217.0e0c9ad00853fee1d64553202559e6d6ae837480.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.217.0e0c9ad00853fee1d64553202559e6d6ae837480.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0e0c9ad00853fee1d64553202559e6d6ae837480","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.217.0e0c9ad00853fee1d64553202559e6d6ae837480.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-frR+H7RM/KxOmgy+Iaokgpkgvpaq4g7iY+K/iSTCQkbLNexfX14+VsPS00KJBYQn2XaFGOmqZ4j1v1RtsZnV2w==","shasum":"d65e5fe6d2f9125ec5a71d0545300f8ea1d0c9db","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.217.0e0c9ad00853fee1d64553202559e6d6ae837480.0.tgz","fileCount":80,"unpackedSize":1846622,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCkSJ8N/Lu2nibQHWAYoWDb+JKHHgaxZPtlVRJO45lr2AIgBaJtAbmkP57SQYDjcyG8U0tIHVVp5c5sCMsyYpslIIM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.217.0e0c9ad00853fee1d64553202559e6d6ae837480.0_1636443281575_0.9870170649341448"},"_hasShrinkwrap":false},"4.10.0--canary.217.61c59d5c905a6880310bdb4dc3ea35f85766a342.0":{"name":"@sberdevices/assistant-client","version":"4.10.0--canary.217.61c59d5c905a6880310bdb4dc3ea35f85766a342.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"61c59d5c905a6880310bdb4dc3ea35f85766a342","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0--canary.217.61c59d5c905a6880310bdb4dc3ea35f85766a342.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-yxxken6RKuZpcyrLhelKausRb8s5MpAr1e70sC0SzfolS8fiucpY4DhuiypqRTWaudHVJquWG7jAuqw8s3DR0g==","shasum":"d3a674dc77b0123b37f2643a9920af894d3468d9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0--canary.217.61c59d5c905a6880310bdb4dc3ea35f85766a342.0.tgz","fileCount":80,"unpackedSize":1846334,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIArZ/njrQuZi12JJaxNENTMXrII+tXBNotsAe787i/JRAiEAsKuQXAFLj5dum7luJNwz8me43FmIQSLRfUsRb8+qWcc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0--canary.217.61c59d5c905a6880310bdb4dc3ea35f85766a342.0_1636443374657_0.05442773298629655"},"_hasShrinkwrap":false},"4.10.0":{"name":"@sberdevices/assistant-client","version":"4.10.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/asr/index.proto > src/asr/index.js && pbts src/asr/index.js -o src/asr/index.d.ts","mtt":"pbjs -t static-module src/mtt/index.proto > src/mtt/index.js && pbts src/mtt/index.js -o src/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"013cf19cb2a0d49be03fd8eb58e1cdb1db2577ec","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-WrZoEmyBlUL8Sq+ro9mpSOHom8vQnrdAMTpTLFEkHnapPexIu7xpQdyAPo7+eSryhygDxjR1gb1PBsm+8/NC7w==","shasum":"24a92a71315193f8fae1df50fc2656f757801f26","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.0.tgz","fileCount":82,"unpackedSize":1849525,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD20WqPYfPPbCltHKFjKhNi/WbqH4bISz7+ZDaOgVm0uQIhAKSVTqBr7sUSwRPnYHdf29+9JOqfhTygtHDVPIcvIJsc"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.0_1636461797757_0.013890621830958683"},"_hasShrinkwrap":false},"4.10.1--canary.230.a5ab34c4c394213c695b690897e09d6338bb2108.0":{"name":"@sberdevices/assistant-client","version":"4.10.1--canary.230.a5ab34c4c394213c695b690897e09d6338bb2108.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a5ab34c4c394213c695b690897e09d6338bb2108","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.1--canary.230.a5ab34c4c394213c695b690897e09d6338bb2108.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-SurbR5bEXij15RyCnUj+lJPxwmdYERgw1Jd8uhrvhDrRs+jEnMXghdfKFetTAepEGhpKVQTmwL3qprUOxOrFLw==","shasum":"9fe09d3e454fc6b5e772da7be65d1a2ebd50b0b5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.1--canary.230.a5ab34c4c394213c695b690897e09d6338bb2108.0.tgz","fileCount":87,"unpackedSize":1907303,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3JJLhGaqBZtbO2ba20Z8foeF5BSNaM8qALB5pRQPpdwIgHUvGU0o2aqSMTao9XbOtGwTlhKs4Qz3RAeF/3Mfvb+Y="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.1--canary.230.a5ab34c4c394213c695b690897e09d6338bb2108.0_1636533895444_0.7245136932387013"},"_hasShrinkwrap":false},"4.11.0--canary.206.c2b117a188bec5503599d965deabf9c0ee264b4e.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.206.c2b117a188bec5503599d965deabf9c0ee264b4e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c2b117a188bec5503599d965deabf9c0ee264b4e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Привет!',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                productionMode: false,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.206.c2b117a188bec5503599d965deabf9c0ee264b4e.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Tlthna55pBWveLDbwwhFmRnEO3hY2Fmf3mhN7Slv+tuXulrsQxfa0nifBCAryrxcpctmjLW3cuBzONzoWI+Qow==","shasum":"b02d2b3de55b5d3169d23e2f5d1abf223da06826","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.206.c2b117a188bec5503599d965deabf9c0ee264b4e.0.tgz","fileCount":106,"unpackedSize":2001441,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICxjBz7PwVhWlI4Y+31jCnSoNZhe3ZLLmdGOd21ub3WTAiAtTxRrzUuP4OxuTPl/uZhmDfI0jpEcmBMqlP5umZuMvg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.206.c2b117a188bec5503599d965deabf9c0ee264b4e.0_1636536476531_0.9547633494579504"},"_hasShrinkwrap":false},"4.11.0--canary.206.81c54b3e32797ed3b0e1661d80a8988fea632559.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.206.81c54b3e32797ed3b0e1661d80a8988fea632559.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"81c54b3e32797ed3b0e1661d80a8988fea632559","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Привет! Рад встречи!',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                productionMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Привет! Рад встречи! | Стартовый текст в поле ввода пользовательского запроса                          |\n| productionMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.206.81c54b3e32797ed3b0e1661d80a8988fea632559.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-aSsw7xwTaiaw6d8wrgY6Gaeo0r0nnBCsM8kz/4dg+vmzfk+38GoDLwOZp5PBPLyRVMruHzN3SjHjCh2wezNNRg==","shasum":"7c5a89e6535f7602ce3feae85ae50fbd790ac591","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.206.81c54b3e32797ed3b0e1661d80a8988fea632559.0.tgz","fileCount":107,"unpackedSize":2048245,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDzV4O6y1hTl4VjhNjXctWRbKlO2kZgZRzxyWgKx6oxVgIhANvgSN33atvFI3j2ULNqK6xfB8d8a83/mWBHbKuNaH3r"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.206.81c54b3e32797ed3b0e1661d80a8988fea632559.0_1636614241059_0.807001139226311"},"_hasShrinkwrap":false},"4.11.0--canary.231.1013eb9ad503f2fff3bf8ebb68f015b75f31a470.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.231.1013eb9ad503f2fff3bf8ebb68f015b75f31a470.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1013eb9ad503f2fff3bf8ebb68f015b75f31a470","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.231.1013eb9ad503f2fff3bf8ebb68f015b75f31a470.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-/3rLMX1SzhQ7h8+XjPchR7JEVYW1BeCWMLNNpGNX3bvIKGzjt2ok0HTQ1XPMe4hO2oD7VsSzi4GlJd/0C+NOrw==","shasum":"80f155fe6f59dcd312ea002aa22dfbeb0ca2e224","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.231.1013eb9ad503f2fff3bf8ebb68f015b75f31a470.0.tgz","fileCount":87,"unpackedSize":1915998,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhm04fCRA9TVsSAnZWagAAAj0QAIK8Vyzs340Itcv79MQv\nrIUAUR1Ex+yGpDRsMDH6f1/aUIGhupX0/MfudH7Bf82MCAtm+6k8Z1Z2+8Uz\neHmYGt2iv7P6cSet/KelJXzhlqFvBirVvPMsmwXtZhk2VXfl87me1ThBdScO\nyhIoLO4NX9E7ZXbtymkt33gP1ClZhVbB/tqK+rsEZ76bicWLkNNszPiqlBL1\nIES+TBmMVJhG4dxHWJE0TdEOYe4JJQvZAO0yBFKL5DCPtBIDjYMiNOSYlYpg\nmQrnZaT48mXmR2g9DnIgxKKafYNl8uQd7/U7LHXB8PuPy7bAQwjs2ck3jX5Y\nd8trsGXSRAHp5n1ygig5l9ZPnQqmuhGE3gtGt5gBRPRbv+VzFtGuuirKwQk5\nZDeSbqsBWByj3XDMscElafqUcOnkJzfA/CC1rKtwLGORhAjPsXZswS1cY4sZ\nwvalwGfOy4gq/dvz5Z1AZ2uJgxE4a9cDXvBBJ6u3iGPi3HHf064V46SwE/8q\na/mY+aLyYYA3Hb5ZczCuoaDpCievl7oXRltMkOveI1G/FKv+KVkKXvBCixJE\nM4JA3DmGN7CXm3zpZJJgTTElSKwnIb19AeXkdKEOlKUGDiNVHeueNv9xhZRH\nAAkJrkgcLZ0tanBQMHP0EtoXdNkNyK2aCw0O8w5fCSZRvG/I/RGc0xEJ2hgA\n7ruw\r\n=upyS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD+D/0hkIjtMbPepFHu+8IJikRm6Cb7G28/eeavOmFIMQIgR4PkGnln3HrfbhocAfDJY6jO9fY1wmAYmqKsN+2/Tlc="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.231.1013eb9ad503f2fff3bf8ebb68f015b75f31a470.0_1637568031220_0.7060835817595323"},"_hasShrinkwrap":false},"4.11.0--canary.231.4107d8ff03c4bc9b71ae9aa1f213ddf599757246.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.231.4107d8ff03c4bc9b71ae9aa1f213ddf599757246.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4107d8ff03c4bc9b71ae9aa1f213ddf599757246","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.231.4107d8ff03c4bc9b71ae9aa1f213ddf599757246.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-BwBRdg7rZDXOkYCfmX1hT07htkAZrt7syEB+Ly3uSFuDicnPrZ2HxxxMKuUz+jRKcpTKStqCQFDKheDAeJGUYQ==","shasum":"b9d490e22d1e2d33e9906a84ccf24e375860b52d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.231.4107d8ff03c4bc9b71ae9aa1f213ddf599757246.0.tgz","fileCount":87,"unpackedSize":1916032,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhm5zMCRA9TVsSAnZWagAAq/sP/33OBlbQoMRgM2OwL6fp\nMDe4Ga+l5KTTar+lkL2NFrpv+UqOiySo5AUaBg3m86MS0JmnPlkRw+qTxVVQ\njIrncmDapBUrzvD7JqghB2pTPUC0jfnGYOGbXiojn97eAEqe9VuSx6/HIz+p\nFdSSF8WwmAuq4yQiNZjVZXuJ9sD53EvPSy+5hR5WpVeCdmVeh38n+4Dk3uVJ\neJqg2nyM/fs4setmVMCl5+3R9RpAIEsJpd5cpR1FPcxz4s3yxNckpWqFKhUu\nYiyuZ6iMaUhvMmIdSmVbSgOqQ6GRgbLY28k/WhZ14t0XrBcYdnS3vCB3nJKj\notqHLoj1B9O7JIknIRooU+Bhl+uTJ44AVFnTmQI8F3Tfgi4AOvuaH4a3Zt8V\nhHoJtqOc46sIFf9RsEhDB9X70Cp3SQBYSfwnEmXpezBciiT8PiEsZJaCo5eQ\nKw+zxw7O8VCWwkj93IeTNCm79LwxsMHZvcOZUayxBI+/Y5zkbCPXmB8wgVIP\ngVt/dMoUu0WFhVPMm4A40mvpLDisr8ZQqnQbnE4WnMGtcO7w9HPYyb5bznQk\nLFPszl60l+TKBrncoCEZSy2scmVqfeqpakd0ZmcbaHqVVF0dqbBpnhMEDlo8\nGXbVtCGHjcDJcK/vk3FRlzVXV/NaMujvaNXl4WvXFIMQh3r2RBSJksoex57I\nwrtM\r\n=KIlk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCj/7Gh3wiQ72fBi2qBkLCbz8Y30fqwLn47UShyBNPWTAIgLJDYeLZAzyJ2hNTwIHyeNhQFcU+uR3OyI/h/Q9bltEY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.231.4107d8ff03c4bc9b71ae9aa1f213ddf599757246.0_1637588171825_0.6546707181200204"},"_hasShrinkwrap":false},"4.11.0--canary.231.98b00847a140e7b375fb91b96f9f107b251720a9.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.231.98b00847a140e7b375fb91b96f9f107b251720a9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"98b00847a140e7b375fb91b96f9f107b251720a9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.231.98b00847a140e7b375fb91b96f9f107b251720a9.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-/Me8Oa/F+KtjlIklqJA9np/IlGsrETYTaiui6mPCaH5Id7WwGS8St8MWMlg4pL3rlgYsMF1YFmzUm86GR7bm1Q==","shasum":"92c14f1dfb703515e65fbb5df216e88bd595dd91","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.231.98b00847a140e7b375fb91b96f9f107b251720a9.0.tgz","fileCount":87,"unpackedSize":1918068,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnKUYCRA9TVsSAnZWagAAdUkP/3khypPbDZh0KZuD4BYZ\nhJMKIBYLbAN+42JQr0tom+hCseDIM+ar6/uSk4wDqMiAeyJBn/AZsuete3tH\ngEoUChDzXM+aP6WVXdu7DehOeBwsmp8R4bU56Ws9QNgDHv+YGFV+CEliezEi\nqiYOJNpWCNArx45/P2bGHHcJiEvS3EsVm0WKdh5C94WRQEDGuzNVZiZzaf7G\nGGntvcNOVPOuSOHoH5qPHIpz229FkY9cqdfzFbwjzIZtJZG0KBrOKGdVyPqF\nusauklre++kdJGt+KPO/L9Td+70epIMln6kwmmKcYhCI2QPQOJIW9KhqOAvj\nrGIttXMQA5BVEs1XhfqdH+eBjIQR9kOo9lTdi/tnAhXsVkYFvo6zELggOlPv\ncGPOhaogBK2ujYnB8w4qDIoPcHnrpUFBruMzCbaYrULaRfgYfyEBUiUUFg5p\nSnM9Ei93hrFAKlgVozxJngahKytVpka/pe+/Ff5cx8n/HT/vU3m2RB3Iobzu\nyxDFvTGL7Gy/HSZjZ2FAmyo2KG77ZZncRnqh4lfYibrx4bzpg4nC9sTxZChf\n9nZ+qg35waHVVns9lTqnDW1UhjdDlpEtgWg0cmJKusdojFEwW6gSIufPyVbP\ne3SAihuwamjENeDDETtmlEzBC9RjG4YYRBgUem43RNCpCa/mPxWHnkL51FMz\ny/1r\r\n=XxtW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDu6TvgoohucP7XY7VxC0y//HjPV1Q8FJIDMGCtBTTHWQIhAPYMrazePhcK/lW3nxLuR8LRFns2zpN0y2HFrkShIfcW"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.231.98b00847a140e7b375fb91b96f9f107b251720a9.0_1637655832515_0.4333314809026827"},"_hasShrinkwrap":false},"4.11.0--canary.231.696dc78beec5f59d5229d3d11d037591775046f1.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.231.696dc78beec5f59d5229d3d11d037591775046f1.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"696dc78beec5f59d5229d3d11d037591775046f1","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.231.696dc78beec5f59d5229d3d11d037591775046f1.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-weOONF1mWaIiz3GfSYQYGexs7i6XdYIU+vQdtL9A67Vp65aCYPEmRKkIvJUEOYuBLT4Qa24BKYmpGGgRd4PiqA==","shasum":"1b6d26622a73a4874d9e66be398a50218ec09f3e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.231.696dc78beec5f59d5229d3d11d037591775046f1.0.tgz","fileCount":87,"unpackedSize":1918084,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnKVYCRA9TVsSAnZWagAA7ZEQAIlxMzMPCYZke7N5epWx\n6cA2tcafi1b9iTQv52nX21opb7HJQQFgBhVqanBUid77KrZvnb1UGwgwFm/D\nOKqsalMnKzOmjkIyc35VWhV1vKlwO5UTxz8VpV62QakaGPQX6AsxL+WRABCT\niM4qlKIKGMIDzICwFgBIQOWzKcss902yWuNwxXi8Gp9jgNhH9tnhU09M8ITq\nlAr+jbIXMCcCr5jRQoinRD+1dbektwME077MC+/WvLhRdcBYBvSuYHzAuoeR\nnFH7urKEqgFeSrALuIgmE4Um1BreGXblyn2b2SCO/rhuK8Xm5dQFQK/sNvdn\nFUQDEuHGuM+HBT6Smac46lHQxp3TvwjDyL7PqZs5fmd+09PAo0fk4Q/UfL9W\niTIckG88oKnBFtDf/S+jOl7PurHUo5EXGPGmXwsS9prbVdW1cSDzWD8DTrPB\nv4ZFzq4LDnNKq6UhAHYqvGQje+kYn53Yp9P8jrKumMVCYUyQbxUwmBeDAVwn\nuudKPo3WcQsMTXrpAKdfLHS9LCEWO5ySjJhEWxgYAbYpsUM2vWI+RCkLZwZ1\nmuNOX2HT+Ia2lkod+A9efuc/1sy2CNYA3ClQVgT0wS03iQ+mozaaD5gkiu+p\nPa//LbW76VdF5WLbklmOPxOyMrmI8gdR7gjCW8klE46/7H1kgGXnfHvTvYrb\nDUoi\r\n=XxQB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG9Ig1+jFnjqodeAw3ZSagfWF1jcLubTercbXdvn1ibGAiAW6iDU63oS8mlUMp2v7o3WuG2IlQWe+XmXkshkBiDQ7A=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.231.696dc78beec5f59d5229d3d11d037591775046f1.0_1637655896473_0.6034430346694182"},"_hasShrinkwrap":false},"4.11.0--canary.231.6af9b36d8a56c338038dcadaf0c7cceb6d1f315d.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.231.6af9b36d8a56c338038dcadaf0c7cceb6d1f315d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6af9b36d8a56c338038dcadaf0c7cceb6d1f315d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.231.6af9b36d8a56c338038dcadaf0c7cceb6d1f315d.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-9XNQ+ClfRj0uMhfIYm53lalCTSiiPZHn0W88H8Y26sZp5p/D1wylqRJBbcs+RDr9qYkmKiqeSqIHtaOJR2H2HA==","shasum":"3f9f15d09a73b9d0ff06c64c184ed8b5ece35e3b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.231.6af9b36d8a56c338038dcadaf0c7cceb6d1f315d.0.tgz","fileCount":87,"unpackedSize":1917830,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnMH0CRA9TVsSAnZWagAAzyAP/2FJPLIxO76BDRTJ+A6C\nMklJaROydRIB/lcO+7qy8EPBxnfSxBitpbXVUmp6iSMpVG4aMSYNFdDCwDav\ndZ0+GAqPWWLIshA3YP0Rw9PsoAjhGkBugfEPZhgbFq6H/0znqifckEmRMlhC\nBjubFedZZIiXEUugVd3EJFbA3Q+WjofkMwSz6IrgTcBHqvwr8Tys8ylS7Wfe\nUkCKhvCjfnnVGc1BwCeIW4SGWFUk6/Txbi0MGhADr/Pt0DuStmUuXeohBUJF\ncdlxXn35sAdt8rARjkHcQhuc1dSb5t04LzIm/NzBJPqiYsRpu+jfAJTW9Zzx\nufa93Rc7+ZuH4iUfFoUTwUbcIDky2g8RC3hx/zJbUG0fekferX9k5gVTopHA\nMWBuGzQ3QMeWN0iUzcsTec7b5mqj5QAp0LU/Wieim8NeaK0A2Cape/F+SuSq\n5UG71wTdRCF13I1NU76xD8wcY7DeQmctANlfYieCrgZ/QHkc1Dx7SnlJydxc\n2/mdTb8HkPmRPjet8NF144zhJICBIbvhpUMVUmwVTRAgyxBpsNdRJHA2HMv9\nYfOB8t/86SWUXJktgqNzLTJz2z27PhwgIUVbWva25y/HfweiLiwWmyDs2m8g\nSv6CKROWibAgAZUwdICN26YbX1lVZgwjrR5YywD4m3E/uR0h4y5ozHjDHHzt\nv0mW\r\n=sqEY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCOUNVdxyy50Nf6QaUBglJFngQ0sY3JSdKRi6qLKqfyAIhAM+ukTWa7nnbtl1yntnP99FFQR2sZ88GdECvTeADMkWu"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.231.6af9b36d8a56c338038dcadaf0c7cceb6d1f315d.0_1637663219881_0.03852841352656289"},"_hasShrinkwrap":false},"4.10.1--canary.233.0ca3f2bd9effb360e200d88bfbbd3c292c54c402.0":{"name":"@sberdevices/assistant-client","version":"4.10.1--canary.233.0ca3f2bd9effb360e200d88bfbbd3c292c54c402.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0ca3f2bd9effb360e200d88bfbbd3c292c54c402","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.1--canary.233.0ca3f2bd9effb360e200d88bfbbd3c292c54c402.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-n8uWHW6ySlIIv/vWKHYggxKHUu4lwkzf9XlrhzN6uZxLCgbX5y6e3H0ZrBS4EFu9DZ39DwxTXWSil71VlxQIdg==","shasum":"743e85daae8a7a351a42905ed28e43d21225097e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.1--canary.233.0ca3f2bd9effb360e200d88bfbbd3c292c54c402.0.tgz","fileCount":87,"unpackedSize":1908970,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnNlRCRA9TVsSAnZWagAAckIP/3pCARPFjuJwLl66lNcW\nrNucApj1uEmNc/DuVSkDLRpdLA6+mr92ku0jFKt0UJoaMwwI75CwUu4srWzN\n0x1W3A3hj4g5AAOMxjCFbVmPGNap1yvgTdoCGi5jDDzWzufXicFONQQWFkQt\nt11JV3JSKyps+INXBH2kCLI61N1WKl+pSc4JExkUCJP83zKhkybBJVJMaaWS\nfp2s/V4nXKcxCMo449FBWoB1jv39/AOV6ZK6BQrlLESDec/cwq65DN2FwHHP\ni0KN2abNes4SzyExjVraasAMnKllRRDrVHfj8aRev3f6w4h6hVI7LQmmKOeV\np5HgkUOzNHwPgFou0g24XVVuw6/m+St8XTL7zyG8xdnGPqxgrR9+4DtvAs7M\ng3UAV4BK02bq/Yv3gTqKS68dAc7QltLv/iX/BbIT5wEW4LG+KUSat9rGy/h7\nfS1tlmWo/CwfsqhawSI3qpmVkQS07irgOzrWUP/rhOR1z1HDVRz4CQYLGSk+\nnnFwhIc10CMWYSswncBuErpCNswxGk8aSzc4y44+tYoZeYRSTzG65RwiXOUP\ncD5EdD9mQwuiXE+rj7RGvPhYBDxbCIFEp52q7T3d4t/knd9GUzd+eLmPFxfo\nHjsOjochOGPkyxOq7G44x9GVBTd/plXddqC87tIwl5jVcrOlVvhSsc3V2p7a\nh6RG\r\n=E4tS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFoLgayzsj3VWVQds2o25ZeLSs7kO8DzKSqjG5T7HsWbAiBDKWeqrzM7xdnujuUFuMyaqSWpPej4IWj0itf5n8t5nQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.1--canary.233.0ca3f2bd9effb360e200d88bfbbd3c292c54c402.0_1637669200468_0.1930650410720458"},"_hasShrinkwrap":false},"4.10.1":{"name":"@sberdevices/assistant-client","version":"4.10.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"69beeac66bcad29211c20a31f911f313b3d70d03","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.1","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-YkhTLNgikwH7Q2j7eXz7Td9pPVfLBltudpWpA0+ffz6/2gReGICctC+ZIMWz6KoTqJ9yiOCMde8rLbcc5aKdAQ==","shasum":"37451bbf80f4b8429f50ae0281477ff1bfb81071","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.1.tgz","fileCount":87,"unpackedSize":1909832,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnOhbCRA9TVsSAnZWagAAjGcQAIU/2wiM7yWfLqCdQtUP\ni/SDs/6NAFmcRrbQ1Drva7rlXITjIXumlrPkFw8mQAZxUT1RFDom5DIeXZdz\nAgaXrgTlsmWqtSBYPj/5ieuLUWTnLVfk62l/l9lG/uHhaid9UVs3GVu/oqkJ\nSTfQ/FdlS5pNtFm2WFLiFTe+VoUzIxKooxBdNZmgbKNA5O2ItukBgSzSXEaa\nMuEYrnd+BjcKxDisn7IKNMRUPjKxk2ygU0QO9S+ar72DYCzOew64Sk2jFCFE\nqdQXi9PBN4VBlPB24j/D5sdS8bgPF9spv4O5NQN8IDCKB9lwsPsqCY5ChYPq\nFpR0mT+0eIwzaGHsje6rBTSXbGmcAD6J5ddp5T/3BBNDQQ8BHyKXwZw/1dtT\ndSvpQCmdeVSUDMTOzPdxMDQbxfF1pT8HhJ8gSUIe8TEDgVbiFYxR5U0OwXIu\nhc9E5JDO6fclvIi60mJA7BSemzTRgmSxtr9Favjir70b37+v29pds6xDCsyE\n2uqsjc8En+k8zPUehteh6A5cX28nyssfxjNFtUzfodLWpUk+S79vFAgTyUc1\nYvCM64zaWAJdeVro7NnSDackK/YiH/xZ3vWdFiwzn1qU/YXAr6eQG1RnJTcD\nPeEObU2fk8nz95ONE6IY6XkgxC0Z93N1k6ulWQNTJbJ8A9+gDpIhcalE10aT\nQm09\r\n=KytT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDmjL6ESK9nmYGzKsvkJ4q9KWdOljtyFZe7haNobTcVDwIhAKxPI75SeojOPS/BjwXWAiPRHcF8S+AaMT6Wwyb72w/h"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.1_1637673050930_0.6886105921271823"},"_hasShrinkwrap":false},"4.11.0--canary.231.85501fd38681cd14b29cc2b69c9567621e826555.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.231.85501fd38681cd14b29cc2b69c9567621e826555.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"85501fd38681cd14b29cc2b69c9567621e826555","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.231.85501fd38681cd14b29cc2b69c9567621e826555.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-G4FDVE+T6rtf1woGn7MNcblGyl6CkE9TYclrFxuJX3e9ZMcB3qhE3sNOoLdg7HxCNMFDE+7o0Qe0ih2uCcgA6A==","shasum":"0578063893c3f781c4434235f9dc46ef3ef0fa47","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.231.85501fd38681cd14b29cc2b69c9567621e826555.0.tgz","fileCount":87,"unpackedSize":1921725,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnQ5rCRA9TVsSAnZWagAArZoP/3D0HIivbKF9tnQWL70g\n9EncgVgWbQANLqHYYabRjL805nGyIQH8LO8+uEzg0nxV+3r0SW13lFlAnlIa\nlxhLnfpTInj3V+vL0o7HCXbhCb2r0hCPwn260aqt7BGobmYGtlW9DQwRx55E\nxDptlMH19EZWl9j4pMRNbDITWJGtW3hztFo5T2o1/BhNwHSa9hhiAgV5q0rM\nm8CHbqs1KgLljIoP43SsSJ8qw3ypKJU4CKenSacMwJqk0RbabgnhZfgXn4GZ\nOOEOJU+Qcg6XPG13A85GldIcJ32sHX6duTRlqSqd5e5DqCiVOfW/wpnNhZAR\nmztVn3jYTHwVZchOH8pwC6Ul7zIIJxNT1+mWDTTvVVKKzaQjmPourdlB4OIu\nJphL+cEgnkX7yw1sjt/ZgRuQFdSvqqBTk8EvWeG5E7/8rYJu2ZCPWDM4/z/e\nBf+I96+lu+9qDrnP55X5SCZzG60EXMJ/Yly0zvSFxaxGn53wcVKGrAC4hS4X\naIn0ZzwRAxQ/Lpo2OL9b7eRtXbhs0J4BMJoTfh1XkJu6qUIsVB0PyzbfGdzT\nyFnfQaQKS+VbAePinDHAeqAI2KBbgCAks7LX4QDh/ftTXytlHRwIM7d1/nuh\nNntrDjDSN6X0STJCo2EoRZWEii/WhdnoUA3iM+BW804RKvtJBuN2K0XInR35\n0wax\r\n=G8AK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDDMwciVKI8R57fDXodswH6BKv+iuuYv8B2hskeI81BxQIgdUbhGF7Bex+XN5y9ZGHDISPpdYBKoNXslxMMb/LWNAs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.231.85501fd38681cd14b29cc2b69c9567621e826555.0_1637682795069_0.2140156429347646"},"_hasShrinkwrap":false},"4.10.2--canary.236.1e2a2d6f0619fc2f49d3b82809a72b7f0e7639d8.0":{"name":"@sberdevices/assistant-client","version":"4.10.2--canary.236.1e2a2d6f0619fc2f49d3b82809a72b7f0e7639d8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1e2a2d6f0619fc2f49d3b82809a72b7f0e7639d8","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.2--canary.236.1e2a2d6f0619fc2f49d3b82809a72b7f0e7639d8.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Q09I/cklaNnZN6uTYyHR1JcqhnPpZOchooX2UTiG/USI5dAeKznzbc1GkEeq1alZg/9CQdpeVmHPoRlJVI75Zw==","shasum":"2f0e7cb1792b47a9ca49dc68d3fe38b6eeba279b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.2--canary.236.1e2a2d6f0619fc2f49d3b82809a72b7f0e7639d8.0.tgz","fileCount":87,"unpackedSize":1910221,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnhzcCRA9TVsSAnZWagAAeH0P/Reyh+WNsFW3UXuNkDH5\nOJsbsQOMJc/o6cBkUUc0OdSasC3elb68JsogfuqttpHQWODwejVFyIgM1nU8\nICVtDCpYEJSzGcdu1/wQZXFWbmFmDzFP6cuAL+HiERUAzg2s/x6xbkw7uH04\n64X/+xGDpLjqYk+PvvOjEjZ9KJYx/P3eutvu0EpSJ0j6ECZgNX0r+AM+aJwe\n7OBGH/xyElymrWpzoxv0C7JY6zPFfgz+sKT/c62gkcE36yAndsOSdFisMOyg\n9wJLF81DJ/YZ4oHKXlnEOHQT8m+XIpye/nbR+zTMsv7BomSyOt/86myQKtGr\n6M1PG0JE38ichUqn0szy+L8t1fmUotsI9v5jza2hXCsoi6t0fm8W/nwuvpZU\nX3mbWXD8+krswY31ICU0kFTnlmbUiESXG5KSRRwtgpPM0dhoQk9aXNXsNMQ6\nSWReFdHWFp/JTdlbM7caScB5QCmRxLfo+xoBRb5Q7zLqDHPKOcu7S955cOgG\nCgN82YYThHjxHSXCetmJYP4ipPe6QuzZUL9eUS7TaAICfRHDM4i6OWy1SP8d\nuhkCYuvgI1wrdlf5mfFqr6PBu4k3+avD81C10ZXYhyMss+0t1Wf1tdSVLpkD\nlzcGHQuqfLivz/prXhRglxDi+GwR/pbtSBW+hFjBCO/GLtPmJgJYwDFvBpVv\nfarW\r\n=HPLl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDaOmryd2ML0O2cBQmKMfBPY1wBQL+ROCzGCBll2BL+RAiB+yVMbEJxm0wZG8P0tozouDkQBEtkjncZ1ZqhgGGZ00g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.2--canary.236.1e2a2d6f0619fc2f49d3b82809a72b7f0e7639d8.0_1637752028217_0.23919433675159074"},"_hasShrinkwrap":false},"4.11.0--canary.206.65f6b8085419a12ba42dc73ca3afa33063b23db0.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.206.65f6b8085419a12ba42dc73ca3afa33063b23db0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"65f6b8085419a12ba42dc73ca3afa33063b23db0","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.206.65f6b8085419a12ba42dc73ca3afa33063b23db0.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Vww3dr5iAjziM0PxudQ79C72iC+PRFvWelFWwG1imgMuX7LlN4QEwUhLUrNOYj8h/UBP2RqAvhs6+J6TcTbUHg==","shasum":"75507bcb7d08306a4c315642750cc00390df2a47","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.206.65f6b8085419a12ba42dc73ca3afa33063b23db0.0.tgz","fileCount":107,"unpackedSize":2050962,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhoOtuCRA9TVsSAnZWagAA4YEQAJNxTygKqqD0u3+QqLfz\ntr4RaXAxWzoy2B0UMawNqWLLKRTxpmLL8gw8q6iwFuOQ4s3C/MATYhPw20O6\nbQWQZV+FdR89rfPmzW2D4Y59t6x3/di2yG4sMoEK4C0FZZ5Sb8Xo4hr/Jvr0\n/ysngfdYiNUDalJbLkjPgzrqP8I+lEy1S4vNw5I22yfXJBP1KShjbeHWzMBZ\nV3RTNjV3i9O72ShoS1HQVtyjZtJ3JjeGk8t4K38oZ0QMOYRFKH/sGbYAfv9Q\nM9tD0YJf4gbBrSSpVYSSuSfpeCd72AAg8oDLp942zhXwnyGJafqpCwratlrh\nAhfdPKzQyB2vntzm1awvw3sFYz1LMwc8PbmaYtdxbcfMSA8apzeoxIXNUgZB\ntcVEIaarmApv/naeDYQZzua+6lE9d4l0wsv2y/3d9fiaPXBI7iPzYYBjAtNO\n4bUOQ2FyCiEEYeJDWzG1TrXOQ5nVm/01siLtJSFmbL0VtBi9OStuQw+MSeJn\n+9DxERMcDR4fYEUR2CSQ6FZ5Ab73egLGg8QZ8r3oriNY+QPCeIiKfNjhmPwv\nSsKxqv++jN4jA6CBsBlVjvmG/FpwkgJYgNSP2E/Sp5e07/EyOZGIRxzKniDO\nPEmVTjgoHdtVgNVsBp74CA2z99sao6eIYdcQnhkvq737OSUoVCukWfwu51eh\nN3/Z\r\n=KvBl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxPQP5uJbtCGYltlYtMwBHXddN0P1Ija9oLS3lOpJi0wIgd0Pk5mhHTw2DfFdXhN7IOlgEA5R/riJYW7tcHnzo2tM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.206.65f6b8085419a12ba42dc73ca3afa33063b23db0.0_1637935982585_0.7401821128865023"},"_hasShrinkwrap":false},"4.11.0--canary.231.7930f764fdb84e2fead72fe15df537272edcbe97.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.231.7930f764fdb84e2fead72fe15df537272edcbe97.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7930f764fdb84e2fead72fe15df537272edcbe97","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.231.7930f764fdb84e2fead72fe15df537272edcbe97.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-sej1wRYro9RFlg4IBf76kM9B+VP1KduR9NveOncy9/tePxDRg1oJcRyQ0RgD3xE/S09eWr+5GGrkgOtS/4Rq7g==","shasum":"79251ef495f61be00256c2211972c477e1e9da12","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.231.7930f764fdb84e2fead72fe15df537272edcbe97.0.tgz","fileCount":87,"unpackedSize":1922720,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhpOxCCRA9TVsSAnZWagAAxiQQAITsXef4UVjIFU9wU2Kn\ng1bd3u0YSmYeGgHrwEO+ZWS66IWtHDRjy4qCU7qxr1PAFaAukPrJLuWLFPlZ\n8cVtPGcnUWkPHyBAxVU2R4pLuT/l5F7Rs7n6FAdPKVstiet3sqeJl1PH+6jf\nhYw35+MhqR6R+d2XbOHyX52iueE4t9PPZTjFst7kHU0xkxFCrBBp4BarlZqS\nLX5EOetM3jiwwvKQC8ZAw66sjes6t3oIJ32v+SCJ8V9aGkbaXRo256WbEuDG\nCR/GxlXtEcMB7+tI8/7tYgzeO8/T0Yx/BanOLlbP82YuzY4QgCQAzLFSHcGr\nuBcNK6ZOgl/7ReqfsA8jSbYrQcERbrbLBvHoANjJBjswPXc77MyIqU2q4EsT\n8Iz/sU3+8RKWtNwZqt4T8IO4bGlCxu3w71Moxayni/R6xUstWDkW/pyTCohF\nlmTZ2nLubBVLvxBBahKur4TKYo4lhk78bF+z9gcvfKG0jW7NHCeY9PuNe+9n\n3DpL+4pRPviDnuDpSzu47x2wX4Bt0zudkNB6zpEPTM3cIvRsvwXnprsbJHGN\n8B22LSW9kTfO83RNb85GgiZdSGKzIuGJuk/bcXc5WS6IkJWG/Xb5H1UuFmNo\neVDXnSWw599Xnvonxd1F5aSdI/ya2ezoJf5LJECfSA4gFRWehcjeqInW/I8l\n7JdS\r\n=vFS7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC+SAwYkqlgpuBIZ4WXqvGf4dDyVehxSq/40PfO/TnQ4QIgb0l+93rheUWgjpRcVo0WbZvkRos4033TcHJOp6aLXgo="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.231.7930f764fdb84e2fead72fe15df537272edcbe97.0_1638198338383_0.5941310033302278"},"_hasShrinkwrap":false},"4.11.0--canary.231.0320eb30e108780a91f5f91824eb0e2e8111762d.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.231.0320eb30e108780a91f5f91824eb0e2e8111762d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0320eb30e108780a91f5f91824eb0e2e8111762d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.231.0320eb30e108780a91f5f91824eb0e2e8111762d.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-H1rE0nFHv7IMimyteEv2IckE9tmBPDVmWbCI1hWgUIicFM0aV+UhkJWnud3LfgA2KKqc9H3NAkNueF5aYys/kA==","shasum":"42042d0c1069b5b3cc331a8579ac0f33c0b9df14","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.231.0320eb30e108780a91f5f91824eb0e2e8111762d.0.tgz","fileCount":87,"unpackedSize":1922953,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhpO05CRA9TVsSAnZWagAA404P/iuZy+mqmqYNnza87u7w\n4auH/kYZZfdWzYt0rD1T8W83s4CtfSipyuzzvREWZECLcu8FeddAgzay4w4/\n0/QkTxfm/XwLrOq2uc0xOgass1haEsgIsXW6u/GFWT2LZvS7yeYy59DOQw7C\n2f9FxQnJp2hZVZxjsvEAgW1Okrh/0O8lZC+tJsyhILVuNlNT/iZQFygF9AI7\nMdq+O/73+025przrtHIYSMXAi4/VCi7FkSw3qCuX9YRDUIbZxVZSx5KbqkXM\nipxLaCOm5uBNXknlQQPMFmfKTn9Ah1j5bMpzClYrvFvCjSdJ78encJrOZx8D\ni0+h6ypKTKbepn5IkynsSartFmtd/PBMy5FBj17wupOBdqwiw4wWtxabppy1\nrqrNDf0KfG3K4zExPZykHyWB+3UwwD7MU92R75fzPD04qsHa43/Omz9lrtiN\noZ7kL3Uc2CBTBmjFGy0GqOIbfb0JsiUNIlFRxeSdRFpjn0OIu82VXjE8GJj2\nL/8D3VTY6S1Z9+Wx3eE4SfyPmWSqR5N9vSyIXfasWkGafN6XBKXFtFc3YpsN\nHVxeipYLfApHPNW26gZA7cmH2HdqUDlKpBlsdtezxuMKttLpcRyv4Hdi9oGS\ncpbRIXGzk6WEVbCyZbm//gTuz2Nizd3foxi1IM/GfuAcdNzR/btNVxIkCUr6\nS7rB\r\n=Wdg3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCkmmcRuneebf8kY2WXfkBDt+AjYEIm43SmG4Rf+/ZqlwIhAMLoypbgvAQM9nioU2q8koNXUfN2fn/sCxQI2eqABZwD"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.231.0320eb30e108780a91f5f91824eb0e2e8111762d.0_1638198584955_0.15872733687079155"},"_hasShrinkwrap":false},"4.11.0--canary.238.e87db38cac5a81f60f079fd459be79f9b99a6145.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.238.e87db38cac5a81f60f079fd459be79f9b99a6145.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e87db38cac5a81f60f079fd459be79f9b99a6145","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.238.e87db38cac5a81f60f079fd459be79f9b99a6145.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-/xK7aMYhMm0A2XRx6q+a+BFBz8h1LVtxsrh0yDtoBmHcjFy4kUHaTYiSj7xmN/7HNdY8Gr5vXOJZ0PLJbh644g==","shasum":"1c71705f0483b42740726f39e4352645d1f9ff5e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.238.e87db38cac5a81f60f079fd459be79f9b99a6145.0.tgz","fileCount":87,"unpackedSize":1910160,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhpdPeCRA9TVsSAnZWagAAcUsP/26gA3b5pK0/wa/Ru9o6\nFhWNGi4wMrnrofoy57FBstxGQsOGIv59KfPmUMdg/edGK7jI7WgrlwAnGUx3\n7ayMa7mOmkm5lpzcAqwbMgToJ5QMPSL0AE9CZ5+I7xYaVbwvKKGeT4Y15lPe\nsGIX4qVRhYTznAFD4uFBhbe5iE3vXKI+wy3FIF0Y/tGb4IThVeM0H6L9A/EH\nonu8nJ0KgapeZvrqENxneoxk14jLr4DAVbvCWWQE0zRgjhBOU5lGFi1P+Bpf\nl4MDOuNzR3fzsO35t6TjLobO1Bjep2vIvApnkRkIkzZSdjgNZQyJAHQrmDlP\nkdb+nLO/cjASWnc8IN9vIieXbCzPTUvOlBmxXU1KvxXvwq100f8ltTD1zN3J\neFoCkOeVKXkcF7dR6EZqLReAEuuc3vxEEuf6RZallszpEYeQ5tQF/rgQiEPs\nfDhsQS/VKB3bDCw2FehGU2mbpxamAvTM0RiOh/XA1JCRwXoa3smiLY47Ba5J\ntelX2vTx/giM34l9mH7uvjLaKU49qliL0GQ3uLgLykvkn8ZLdZ5HqCcWyOl9\n8tnSR7XzEtKzBIxhVLp4RSiMd2x8vigJgUBXyZOeI9PIiIDR9VdPrN5MmySp\n5TiW7fVJmUsuFX60v4HHZbSNbNbLLTqrn2Ov4Ck/xQVq3uyy9j8t7Qhif1Eh\njDd9\r\n=Kg+M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIExiu0QJyQmLAQTasXLCyoNtWk94nfWYyPiHPhsSQdpyAiA5WyzXVm4PLBr0w/ldPMLHrusXE3S/5p2u1AB/alu+2A=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.238.e87db38cac5a81f60f079fd459be79f9b99a6145.0_1638257630314_0.6474964720576206"},"_hasShrinkwrap":false},"4.11.0--canary.231.f76657ef84ea8af1cc9cc2aa02f1463a9ffc538a.0":{"name":"@sberdevices/assistant-client","version":"4.11.0--canary.231.f76657ef84ea8af1cc9cc2aa02f1463a9ffc538a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f76657ef84ea8af1cc9cc2aa02f1463a9ffc538a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0--canary.231.f76657ef84ea8af1cc9cc2aa02f1463a9ffc538a.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-2JijJ5FKsBMd5LI3UCr5ORGLfjO5RIkhvGL5oncOQ2kcTklsJXznkdjjtWowRn+gNRpnKlxXkp3b3UHUphRKWQ==","shasum":"b39dc753f192746e5d9a5d3c54f028e82e9136c1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0--canary.231.f76657ef84ea8af1cc9cc2aa02f1463a9ffc538a.0.tgz","fileCount":87,"unpackedSize":1922953,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhrnFHCRA9TVsSAnZWagAAM68P/jJjpB7XE259eWFmanqE\nDTl9hxINT8o3BH1W3SaHZGwX3ShrWtVTYCnrlikPttyFawUKLuGOu/o53Nd4\npDAkcpXbuPHXBwcYYik140blAND2sfeH+nlfGnLgQiaai6/oqcZ7Cdp7XpFt\nft50EiKSUfr1bkJ+zynJXQ6/M+cX0sG2mX7TErn/miMgoOhr5XkVV++uPrL5\n7i40aITyMi1u08rFcAl1wmxHOYZCn9Qjd9dn9jsWJpx+3dCgywxLQGK/k9fL\ndaJeVqycLBY+VxwyWzVOxIm7pKPP73wz8bSGkM5uguDSJnwJWwDjEWVeQCLo\n9u0bM2Y9AGXIuDdBv4wKGvPkUi4ZwkgV3UzPvAJ4pyW7HmBc8Ciuhq4VsgGD\n+4NRdNMYOSZpB8/W6b9zsueUabQivLTa2fGUQW7Kju+o4Rz451rhSlLQ4Iie\nOlDoS4N1PQy6IxHISpqnd/h7qH36lahmdOVvTMsDM7EJ79ylVV6qiLHLu+V0\nZVbLnXvg5qsYk6jLfoklCtxI0mjZ9a/z/QKOHipIY4giX9hZSi/hxMIXIuUn\nU9eWuyuMyLGD6Frja8WT/SS4J9fssh/t9UQtzs2o+MjtXS+9hdm4/I2B8dwa\noaFdJesyJ2VjbAfA72fXanlU6tlqdnduyy2DRycRN3bJ/ciqyaLW9U2OHwu2\nkIrq\r\n=Z7dc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBhWFi7rsb2RW+vBKKv2LNRkDLnVA+N5FvA99yit/b8IAiBDFVK/gEVBcUeu/QpLtegrqEomO5MvNb7cqJUkwt9d4g=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0--canary.231.f76657ef84ea8af1cc9cc2aa02f1463a9ffc538a.0_1638822215455_0.2301542342980054"},"_hasShrinkwrap":false},"4.10.2--canary.238.308aeed034f9f168a331cf78408e2838e1667ce5.0":{"name":"@sberdevices/assistant-client","version":"4.10.2--canary.238.308aeed034f9f168a331cf78408e2838e1667ce5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.18.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"308aeed034f9f168a331cf78408e2838e1667ce5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.10.2--canary.238.308aeed034f9f168a331cf78408e2838e1667ce5.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Q1RDSVer8fOWOXM205iqBYtY3BW2kLqHWsK/MRFJ6M5SiClBTZOx85MKJcgBqaLy3vdim7qjeLZPW30BL0vzqA==","shasum":"4dcf8641b6b56f5e64bebff96ab494a69dc9df80","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.10.2--canary.238.308aeed034f9f168a331cf78408e2838e1667ce5.0.tgz","fileCount":87,"unpackedSize":1910782,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhrxygCRA9TVsSAnZWagAA0vAQAJ/b+ogUtmR1ppt5WloO\nApWN9cuwujtb8LRzZGQF6F34WgSUhJL2AN6SECA/aWWNVFppM8PhmlSDTmI+\nlwFQG4/5VkU1rsO40FJIfQ+2x7ETauYZUdFBj7Z59j2yOmzhcz4EqEJkVNzt\noF30sSm7KQdSwt5+Rn6uqlULWZyz6cBofcFR+Fo1xh8JR/Nt73T+weIyPYLt\n2YO9avijAhsxNVsCRAx/6bjpJ+bptJo8/PU84okZgzgDEAuRMxeoQaplglJ0\nY6j0fVtY4srCdNsc3vCch2jyD1L5SXXKJ30WuP32a5dePAFQO9eieG6yGKf2\n78WX90m6Q0yb43yoogHov3uotz6CyxtZNjZjonynqkLoIkRfrWFC1v/hmHwW\nF8/wuIfGZgt/5YyR+Gf5M6c5UkwWabUGunjedhoic+pf/w7jFUQZkiY0v+8i\nu1LtpyhfwV4coCXn8I1zkHsUhdtuaaygecETLBzCjs+5KlVWrHCSlWMRFY/F\nKJkfKbNv0ai+0seOXMNlkT2Csv27qik5mUd3nuo1UGOjW5WMJSS5MqMSjuFR\ndNExmEchWZ7YVyK0p+RUVTbNu47N6gYuevNCj3BHJu9HECkn2v3cc2r7X8f1\nzJDlcBbIOLyaQwNlyeOlEsBuyeOYS+YpGA9RsU3oZbZFqdzzMtsC1YY/AXA9\nP0HW\r\n=+Sh9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGNdUs5gmX1VSKfYcgD+uFcWYtw8+hI20hIE+NjWfv/gAiBouRTrXRkYEOuB3zlRTgtP24P8LDiD7uQg3nvd5OeMpA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.10.2--canary.238.308aeed034f9f168a331cf78408e2838e1667ce5.0_1638866080531_0.4522264454178344"},"_hasShrinkwrap":false},"4.11.0":{"name":"@sberdevices/assistant-client","version":"4.11.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e2d32b79d6581b8014208c473a1724956d0cde9a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-pvqmxS0flfjZtQkmTnG+JSG0GrUuBOQVP9TiNbpDhA9RwVIrOF4PmMvxpamqo0/wK+6+PhvV//z4Xm+pzePngQ==","shasum":"e3cd0cb6f1615f99d5963d0df74d67c3a4d66bb4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.0.tgz","fileCount":87,"unpackedSize":1923037,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhryJECRA9TVsSAnZWagAA8NAP/16WKfQTKXFo0oCr4ECn\nFuoQcG1XyG5bgjAk3/Bt21vAZuM9ZeHA5xi4cEvTxE+jewwJ/NR2oignSHeh\n+wL5cSCYlWzspvyUA9PbjBG3YstxoA1d0ZtEum3bb/piLOSh5zl8pFVLbVvT\nOwoUAKrGfu4Juk5DhcCdEGHgY1tdlTZN9EuEE86suMm48k2VXrkZjwV2SaNO\npAPPKrwXspS71gMVofbyoPsj57NjmzI2uRLbwpYUsl2B2fIqxQWsvKdqSblX\npQjf233+mzAgaK3mH1LUJaTBY+fFCws0u1WGPIhuwBPlOW0suxPapUUCTP5E\n7iA1GFfOAcolW2CQgLu/64mzLmwP4OejGW8+JyiG3fMl5zo1tpvzLR8eV++Y\n0BA/5iuvDF7eAOCMwIWJKiIThv4n4jgZvdhxzQ3zTKFcaCmdWgZmCjQeinwF\nM0gdgTKpLl9jYzlDK/bQK0N8WIxjywUBKsgMOqbO8If5FikinKUueFZwInQP\nIK6/9y/+3R4XoxrStSTUKOMog1GrMKRCEcPwyrunOw7kcvOLN8V5tk2PPxuW\n65yj9MHAyfXDQlIF3ZDXSon0GFVQQNAntNrywglIVj8BRBh1x8Tap6yoJaq6\noI6PRq4PdDozWHYYqpSMAS+4fIzdltC+12kN/20ZVCjT0G+91UGe46tGBLxb\nsuXR\r\n=VOwB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFqs/ShNrVjMUUfi/YYEX3GbtOJZGAVcdWzr+U0Ckrq1AiEA5FxCGoIHlLJJOEt5yZOoxe96R0b+yX1h5OAEeHHcJ0s="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.0_1638867524295_0.0283959910901741"},"_hasShrinkwrap":false},"4.11.1":{"name":"@sberdevices/assistant-client","version":"4.11.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d43e6a0478001b558f7bec521210f8907bfc171a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.1","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-g1tP2CSmGwM19HTtUo8A/ZGabvlRaMNxK0nAYmdngLJH4WGMeqOO1+TUVT6YTgoxX2hUbPBX803tH0BiBMPcYw==","shasum":"de0e6e6b28ddf4b9f304fdfd3ff61d43f4321868","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.1.tgz","fileCount":87,"unpackedSize":1924134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhsLwCCRA9TVsSAnZWagAArlkP/2BdKj670F98yjsWdZ9o\n33sXEbs2ATbnOeDJBg8QV4ox9HPD/BtUs2GM5hIc1B+u+RAGhCSwkqnPR+vJ\nE4/o1FNNt/HZbhbDaXj1HgPCaLGZReF92ynYOfm7TODFv4hRpQ6UqtXTgk/L\n+ILpahxL6t3AFXPeDw6cANT4ftwgqTqsv2gauYPta9zhi+7Jw9el2VRzq0io\nMxeZjfG1sZD/0OMokuAl4FTH1INqkJa+Kht6H7IoGPkgG2/wTwEgZd5CQ+nV\nqgqCNpqbI3xoZX7XQLnHCEH2sCSQSZUS2PnTIjm7TgYj+CP8xqtyJZyeSGWY\nHMVfjxPTCA3y5xk88MrsGfjpERgrGqcJvv2AyaZwmoI3q4tcCHH/1oJboVK4\niVnOlZObst0nNQrTj91Go5lq5q6vstomz48sNMRI4niykwDqPeSWLMNQ39tv\nf5QpOlxeLA9lUI6lXu/HCLQ8m1IuuCyZpUJUuAbO7XZf22qgqqM8qzJDAfkq\n1BTECztMtTKPM64vBKSSAKpk2KOjzcn5JggDK1V7dTRP+AWIkdcESkGgpb1Y\ndAEoITJ2X/kvVGjHxeZOTKIo50u+H4OA0uhhUbta1Hg78YOFGD+D7852b/s6\nOFtYbjwBz+FIhdgzMUj2WxgkZy2ezlr+kI8iH44C6rbGr5pz74fNTsKOsgQ4\nzLqi\r\n=Ff8K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCH1kQCeLfZziKLV+cdoatoqcR1mPPzo9zgP9leDnWQigIgUUoBbAQQScGIr1bqz5hOBQ0ty0F7vJqQr1GLDoJXPMI="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.1_1638972417967_0.6994353602696559"},"_hasShrinkwrap":false},"4.11.2--canary.239.a04e95f7b1864ffb4b1fda313042e452939e2aed.0":{"name":"@sberdevices/assistant-client","version":"4.11.2--canary.239.a04e95f7b1864ffb4b1fda313042e452939e2aed.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a04e95f7b1864ffb4b1fda313042e452939e2aed","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.2--canary.239.a04e95f7b1864ffb4b1fda313042e452939e2aed.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-/wtTFEYCCC9U/TX7IZUS+tO1CNfPMXSqsZ/Pv1fo+9ssxS54RCizNEfIDK4wjAycKebh/nha1w0zK3G0QpR0gw==","shasum":"d120c292d685da943df4f4fd6172f2ea72d39a53","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.2--canary.239.a04e95f7b1864ffb4b1fda313042e452939e2aed.0.tgz","fileCount":87,"unpackedSize":1924390,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhsyYuCRA9TVsSAnZWagAA2McQAJgITxwfJ1yKRH52+kW0\nqFU4suNCvGJdsB1uBwxyWXMYyGkvN8SBAuC6wZtPUpvLjhVZb73U2TooR+Jd\nC2rDw/G1XdYEPxid1Eap6youcDeXOgzXM4rfCYBIo5f/+5eEKYN3twEAOlW9\n1uUnIQJ5VNaw7vi6Ao1KVWf9kypw8HQ1wB4iwjumqIp3dtNHAwVKw9IDe4jI\niaYLQ6Fbxlpjw9fi08ugcCWYcisVuDcvmhOq4Z33AcAs9h+J3hlcwW6uSoBM\nkzULeQZRzd30Vxd86G0rdrfUwVN09rNpRReQkQVoBRHbNmjHH0296axXTBex\nPjMnPB0dwEssaN+L73T7OllEPfZWz3tUkMMAGzx5oHcaDvA8o0LJXgXyTeFg\n51I0RZTT0NJtpHzAt/Drph8tWrA45wCo5ZH6H07vWKASl5m5rmqufQUbIpfm\nsIBgFB0zMY+4sYeRSMi4gixRIRnPVvuHCvgglBGo3Vw0ud5iBk4PvuYxYNO7\niCF99/7jIyeJL3rZw06sVweI9cyqCAV7CxVwCtE+nO2GQYuG5noxl/2TlbiS\nsguTBObpWMYziCAX4/83GmjqzHKjz0U+DHnOvVd5PGzqTq0cGGAenzhxc4zA\nocokujmiPopP3LBH7qdhx1/ypfv9I/mSm0BvYIEd/b1d2ypmbnvNQIrOGikd\ntuiB\r\n=H0ND\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQChYKdcDB6VNAQkLhfJquLSYKc9WigODNOBqxzvV7/QKwIhALIDKYkzy1r7BspiZPpqFLhPrt5MKw5gK2gu4rGyrUay"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.2--canary.239.a04e95f7b1864ffb4b1fda313042e452939e2aed.0_1639130670756_0.7821116527724754"},"_hasShrinkwrap":false},"4.11.2--canary.242.3cddf26ae59d110bce1598e34dcc1956e66318f9.0":{"name":"@sberdevices/assistant-client","version":"4.11.2--canary.242.3cddf26ae59d110bce1598e34dcc1956e66318f9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3cddf26ae59d110bce1598e34dcc1956e66318f9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.2--canary.242.3cddf26ae59d110bce1598e34dcc1956e66318f9.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-sekr5gbFBJIrbjXt9E1CM/cX+rlKR/A0Al8FVavUYWaNT3FGiFgdyXRH3jh07NBrKh+z+uIn0b07uMFIEl3aMw==","shasum":"d9bc01e5fd4a1eb9ff2606f7110ee5c834606336","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.2--canary.242.3cddf26ae59d110bce1598e34dcc1956e66318f9.0.tgz","fileCount":87,"unpackedSize":1924507,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhtjJcCRA9TVsSAnZWagAAXy8P/0fARm7sFmrpRbtGyonU\nNHQ8Z1eYt7dufboEBv0dFFohSJq+oIpNJMTNNnJv4DMUlBIiiu5m9IuBz+rW\nU41WmpTYFKWVLZzt2zkKUU5dvnkzjdvDNCT6ramWIk8npc4atJTDPyYS6/ew\nxuLKR8VI7u6pknuW8XL4+qvxtS25c5sLHz3cucz/NngGCC8F+6Eg+RJlD4F0\np4kzkKhpWjBu9MEn2ZXaAIccv4r7t+ciy4oC7jcfhI6SidMmoPwRESfQfzow\nyOrIjInty1MkCIRMuCJ0NLldgBnth3SSnxnAtEowQ9vCSFrBtQ2L2cGmC3Cs\nWvnI//HcYrxx4uZAVnDa1LYPspeF9s4iF5wZB9m/GNldXAe3HTuHIBlSua/q\nq6DEqo44h4d3CZFA0EnEQWpPb7UwbsXS6UmRo0GqBnpRcVfGYmzg0dkVBFCz\nRlh7+I7GwGV0gqsyTCt5PUc5T0DVYswl3XSeAa2VfUT1CNqs6B30Yb5sRKp3\nf+TXjlEF/A2eUryyIY0tud6aZXVCf2z7FrSaHpNgxH/KukMlosyJ2ZboI2A3\nKoRmag7C8qFZX0t8FPSldmjMIAaUkADpXqN7i4QO3XpeNpfFJLAa2+eBmCYq\nUqMazpxI/rU0IYfQZZ56HuEtDDM/GL2L1rKJWTLBR/4GNJs7RXeyap6/fder\nI713\r\n=8M4s\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAeTGApW8/zUW4iiymeWArvZsp1izlahlcVg0hEG43fRAiEAibnSoJEXhk78k1z2UXhMbjUCswEFhkeEBTWwSx2D6j0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.2--canary.242.3cddf26ae59d110bce1598e34dcc1956e66318f9.0_1639330396449_0.9754570065048289"},"_hasShrinkwrap":false},"4.12.0--canary.243.6e737302b7f39b76dd39d10498c024cab9fec055.0":{"name":"@sberdevices/assistant-client","version":"4.12.0--canary.243.6e737302b7f39b76dd39d10498c024cab9fec055.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6e737302b7f39b76dd39d10498c024cab9fec055","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.12.0--canary.243.6e737302b7f39b76dd39d10498c024cab9fec055.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-YdBL/RDsb20smjJdq6BDuIlWZOR5Oqf79o8v8O0AE81rFav2d3NLbn6qQ8n0wudRGMKVmXqk++fMyBqQ8qVZeA==","shasum":"feba8cf1f654ed268a315fb6f15c667bd9c4fbc4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.12.0--canary.243.6e737302b7f39b76dd39d10498c024cab9fec055.0.tgz","fileCount":87,"unpackedSize":1924507,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhtjLsCRA9TVsSAnZWagAAhUcP/1UTOoq+TJQ/WOB33Jgt\nntE7mQ1VydexoXd0aWzaLIPrrT6HrGh1VVEwif6ZfGmHk6fxVNvQc+xc1ZrY\nENrTwA3ZZ4QJTnfOcBqH6TVPvoG1a9PSxbyVObtQuWBO3W/5J5P1MZu9osrh\n4/bvebM3fMtZ0WygZUc9foC+/RaKBGFEn0yNfewI1KOLIXhVGpQXLWwHxjP2\njQ53TP0hXHorM1tAxS2709XGUJBAWiIwIGYSdnrdynhoORBg/XevpACKMxht\nwq8ukJxDsssefJG3SgcljXBIEWDc/xdBSIt+1eM5jgccZ+vUXg4+bQdF6AeH\n/USoNi1Qi23aaw01OkbQPrNiKmb7/PWdwcUhqyGXmCGAXIRNo4vxxmR+GRIL\nd3eqgy62Iuu35RdhHc3d+w/XkEf9HUHew6QUAyVRawJV2wlWiODvwVcz0K0O\ndH5SaywF25c30lR7lxXmtjegeOBNBhVgYdUDRyv1RJ+p6sHhy8VNzIt78KBe\nmGmLrGpye1WQC37RwhbmsDRFhoBNgMfKghewEn3zYze9e5cgGhDkL3Wq+z/R\niMAp9Tutj+YCQneDYsetEmOJEAo5ULTHkgxyl1FfMZmGoIf/gf+0IKzx9H0x\nARfa9BYCkOV376GpLE/3qC2TJq+zvvEG3PckEfmij8ZR3ulMNmqVu8zlrcDR\nByya\r\n=IHbN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8dfcGFSFDXreMNVgixtmEn2EDLQ0jp9Syq8A9MC0P3gIhAKlJ1e2IinpX7ROKhTiVN3IyJ8ZfPKRDsNEIN0Fx7h7V"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.12.0--canary.243.6e737302b7f39b76dd39d10498c024cab9fec055.0_1639330539935_0.06406654043172288"},"_hasShrinkwrap":false},"4.11.2--canary.243.0557b298f716b4cd69932f5055522aeab38e52a4.0":{"name":"@sberdevices/assistant-client","version":"4.11.2--canary.243.0557b298f716b4cd69932f5055522aeab38e52a4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0557b298f716b4cd69932f5055522aeab38e52a4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.2--canary.243.0557b298f716b4cd69932f5055522aeab38e52a4.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-alkCv/4c8K44Xf17TPDscTtsVsnizRYstseUb7uNu3C8HHrhronPjf7yTEOqwc9ZzGGoj4dbd7JzdLlWwPNX0w==","shasum":"aa4f99c727c3697ddaef01d93bee28c77c66e146","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.2--canary.243.0557b298f716b4cd69932f5055522aeab38e52a4.0.tgz","fileCount":87,"unpackedSize":1924507,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhtjRQCRA9TVsSAnZWagAA7akP/391CEUEoZ3yQLH2HQ2r\ni2075I1YjL5uQ6sLvwlOHFrJkACkvIbn8frDjJIvK/75JS5u5cXmqCbFBf/x\nPAzdws4bQzNZm0lk6pEw5xAaPWqx5nfRoyEhKe9p//UcwAP5jwBdWZ8nnzop\nlVAuR/k9myUHC15yZTqE3jmwzG/68ex4NnZJFj1ulVGyNUwuq4+0rDLeeX2m\nhhVpl+aSaj6wUS7Ob9WQIpzHkCdi622QckmSddSffy8qS+EE7hiPvqrgri7v\nwVHtUwnLS+l8pSEyxL3kO3L3SDll58oI30kthOV3EI+QOzJUsq+VjXiIJ4mR\nmzyi5bcKT9bh9SVpAoFKuTZLVMEcg6KxDaNt+vFXYGaklBsyEr/pCqUzefau\n1mvD2ksRwKK8c7teJ2h8di83rLUhH0Nntqg1uCHXcrl9mrBaJJav7ZARU5D6\n/0xL5XwzQ9Ch1PxJqhYrBZXU0+ZUVnb2wn7IUa1gTrrXd0xU5bm8Vrk+2Doe\nlx5CQcOPmJS9X4EwJjk7d71oW61FX+WErxbZ/yOCLTHsa5e0HK1Xokm/X5az\nJ37suUOc7ZJwK7uRguBqxn/R8u/dYUADnyH1BA+RZoekm6GimtdI01vSUyfO\nGruGNCbZBzVuUHdFrFw7BkIaXr8yNhOeU/QlzbPGozS2l/u/CYaJcWpnUMhU\nLKFf\r\n=W8FB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAYoVLP3jUM/6ut763L59gEAKqPyTg1O0iD6P8P39Tj4AiAxhIBA2AsWKRYC7BEDqjF/G8X/tiSWimq1gPJE06or9Q=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.2--canary.243.0557b298f716b4cd69932f5055522aeab38e52a4.0_1639330896256_0.9819754065204762"},"_hasShrinkwrap":false},"4.11.2--canary.246.2fe096b114c34902562a52219957cb8400036448.0":{"name":"@sberdevices/assistant-client","version":"4.11.2--canary.246.2fe096b114c34902562a52219957cb8400036448.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2fe096b114c34902562a52219957cb8400036448","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.2--canary.246.2fe096b114c34902562a52219957cb8400036448.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-rtO3+qc7BYsxL8YvFzclOFYNqIpf3eHletboumlMUyCvwz4bO9oGSKfl3jGY6U3SDYYAu4Gda4TSVNcx823oyg==","shasum":"cc81a91e76013b1e9229fb581583c1dc5a5c3736","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.2--canary.246.2fe096b114c34902562a52219957cb8400036448.0.tgz","fileCount":87,"unpackedSize":1924139,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhvDN3CRA9TVsSAnZWagAAWcgQAJYVmxUpjHDqZkVibzZT\neN8EvS71B51bQvyGMfWz5dFDQpmZmsIvNx0fgreit080NiBKCj1Liw0djmGE\nIDzopCogTXuCYG+zC1FCXBawBSaAgize4CLOtN8KZAsH915m8ADy8rxDaySA\nLIKwsrLD2bWFP1qGBux7kkB5CaD21ks/undSTGJ+tTrXUVvFt3JgumaOOgjJ\nbK95gdFpLo5TE5T3I9IYnkwj6L1GVFeEEUfxFnWD9xikaZ/oHT7a7eIDGj27\nsQ98cqNYPIi1xiKR67bxUBXhjEYn8L6KoLojYoZTfqzviWyXePfICIqTq59x\nvjRxoidB8eWVcIplE7woPgRnuYHQjXPOr7Pzqrh7i/UpOuKCVf72ZcKToJmO\nBKQZ67Nywdcxiqv94vDtwU3zf6XSnTrvPeDfr4LbSMz15xMEoKMCCARMYiss\nOgtVL4Q0djuLHeF5XFJ8d7NjNJWYJJZPgP0FINnJ/OZQ6UjnbiT+fxl9FEoK\nPu5beqxjkRhxBtB9iDZ2RBOAROzA7zlA+QJUunLjgYnZaS30mqPKqkOq6m8q\nEUwjxjTfcnlFOMM0IBezLY1Ayq6BkOE5amKglE3gLXhaQIKs9BXv+JLO0SqU\n+hbO35GVw8z5KSEf/SH7/5RFv5NhPCnMNA1SsVXnYKLrs4iTHOqy0+yeeXvZ\nC7lx\r\n=jrrS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDY3hAULQha5M1/amvVktvfEaqCIBRkD/PPso6ahu/lYAiBMx4gcu5m/ZZAThMf+DgwvJJa2NFNLBJ3r1X5uU8yg9A=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.2--canary.246.2fe096b114c34902562a52219957cb8400036448.0_1639723895682_0.1259863479645864"},"_hasShrinkwrap":false},"4.11.2--canary.246.a453dc19ec9778a2e35779b65f37ffc7f85ddc97.0":{"name":"@sberdevices/assistant-client","version":"4.11.2--canary.246.a453dc19ec9778a2e35779b65f37ffc7f85ddc97.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a453dc19ec9778a2e35779b65f37ffc7f85ddc97","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.2--canary.246.a453dc19ec9778a2e35779b65f37ffc7f85ddc97.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-ysxmr+qMhHKHZlmdpWYd+X6k2+Y+5Be/K7k1bYKLAGnRsOlxnXG7AJXyIEDUUVVeLfdjrZDA6cJzru9VkkG3rA==","shasum":"3d7d5ac89259d26bdea5c0e0dd0782ccc4e6383f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.2--canary.246.a453dc19ec9778a2e35779b65f37ffc7f85ddc97.0.tgz","fileCount":87,"unpackedSize":1924484,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhvDTtCRA9TVsSAnZWagAA5zcQAKR3Xbj8I6M2lvsgOtQs\nuz2h2L7nKr8dGg0yXF07U70op7YITs18pE8pyaB7UXPd7m6raLmGNEjrLizZ\nHXOrPQtkMr61LY44UNWDOrX8J0PmMfehJ0dVRPkLi6Wf/JytZhvOoyO7BNRk\nuX/x03zXWsRbHgW1PSaWVR8cAjSn/dgyoM68M1uX9oBu2oOe3hn265uvmGq7\nk3mkyIpY6doPu3eqDRuJYfnxbN3J8SqBtlj3ApPDYYpavzG9rLhtYI5PCthU\nunIwjBttoaFnX7qdi4PhVKzZfArjnfNJ/G3aaaYCGr3X+FKMSCsD0OSO7iQw\nQ5uCJxc10hXAeKyos2/Ra82al7kzlge8GajA2Nb4znCVjeiy/wjjAIfEHo1R\nin2q/qLiOg7I/pCCw9YFzmQPw7xYKtHQB/7QZj57A0wPdKaNKc/abAh0d8NL\nMvBueN0GVK+/fv4m2jJe3Y+hAjhU0Q8rIYBvX4/ADwdpG2PPQM+bB1mms3V6\n4N9ocvOd+f8pOutt30exzfwc9WGx54wo1dDb+uXrwCm8CoMc+0SfWRwC+7Z9\nv4jsvsDhU78A7BV8KyazbzR3rew0RuyVqQIUPF8goyeIMi/YeI3nQEdO+Lvv\nLpYfViE/9F2QUOgA25jSR96yt5o8uWoi0F7UPwKtCicxCsO4buye9mfW/NTt\nQ3zJ\r\n=Cm3P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUbLDfMredH42dD/HllPqs2o0y0cnTGbSI/z9lM9S7JAIgaRf4HHzLKVxKmUiX83Aydxn61VL41AYYUS4eTCQsaQQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.2--canary.246.a453dc19ec9778a2e35779b65f37ffc7f85ddc97.0_1639724268926_0.460878133973301"},"_hasShrinkwrap":false},"4.11.2--canary.246.98d032e125bd92a3413082d25a0b6e7639ea386c.0":{"name":"@sberdevices/assistant-client","version":"4.11.2--canary.246.98d032e125bd92a3413082d25a0b6e7639ea386c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"98d032e125bd92a3413082d25a0b6e7639ea386c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.2--canary.246.98d032e125bd92a3413082d25a0b6e7639ea386c.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-KAOPYr2YUvrEYWou2Mv7C/UwB+gdgZzF4SfYcyiUru2lhHNLa2Q+QlpdpXNOSbxcmBjXRoNfTZNgf9hvQx3qnQ==","shasum":"104ef5eb22ed7f7b357047c38ee1226d3c30bba4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.2--canary.246.98d032e125bd92a3413082d25a0b6e7639ea386c.0.tgz","fileCount":87,"unpackedSize":1923739,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhvDVGCRA9TVsSAnZWagAAg+wQAJAAKUVaPX/kmoW+2zD0\nntcHHYhmqZxEPa48UOUNPT98MOJ5BnjzxggMxkOV9EeyS8KT8NeRmnKH9YfL\nt1dWgpLfbPe1orFaKo/pGJlNiYpbtBMIjI5EJHaLys/N41aumv0GTVA0EV7a\nyO5hqgZGH2X8tSOs4VDaknxTDq7V8uDW8ur8XpWsPCUmqzWjIJLjgMXeEwSN\nhNNKhc+gSGIQAW89ibUKN90B+yXTJYP1SilS62X0VRU8QiBkfrWtU+OWtIWs\nakotB4lAyg3xvcADPu3Se81Az3O9RYykF24NW/nc2ivxjBy6Hg5cY3LB5RJs\nKM3XwfzgrVftu2qhKWCFVUqeMYdv6rjlJWqqJ6raiDMD4Cwt099sOf86Xf6F\nMi4d5xexkLV7+c2JN0FWVxUyUP5NmKw70gqY2Xgw2splM/jsqMFTvCUX5/8z\nOC5caGxyrfqEY2FhXi82vs4OugXIDoaNjOiLWLlcz/3Y/SSetQO93OPuXQig\ncUtkXsd/jnyitjTC17cWdCaNAp4lS72kGFONrrTvx1bd6bA7Wc2lyzhEV9dW\n1JA3Tl3zbFF0yr8ely0PYpRgRsq6ZrJTicfrQKnOI3VUbEvXnoj1pKmcsF5K\nMdP5zYKbgPPGNUAVvudHX5f+v+zWh+Q/4s8M8cfIqTHbs1addpqfg5/CROC3\n4565\r\n=SYY+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/MtKZWkeb7r9kN1aX7+xknOoZ8+DgT3+q+HQUUYqimAIhAOyJEf5SODbvPNho19lq+fjulv0yOO4JHZAZmcz65Q9y"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.2--canary.246.98d032e125bd92a3413082d25a0b6e7639ea386c.0_1639724358449_0.4152527594808666"},"_hasShrinkwrap":false},"4.11.2":{"name":"@sberdevices/assistant-client","version":"4.11.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"460900627b8ef10667a33f9d828bcec17c997ccd","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.11.2","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-MS1QquVaX8ze7fBv0TL8C6QxpO6JtDsv3erQun9UTNxMn2KZ1lOEZcd7lqyaqL8h060GKJIwDHBn9stgGstMmg==","shasum":"e2c51cccaf97ef76b80bd0b5d90ab508a72b3d65","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.11.2.tgz","fileCount":87,"unpackedSize":1924328,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhwZOuCRA9TVsSAnZWagAA3IcP/0CScDPYJpJ12uJ1RfxE\nNgLxe88ee+e2mvNpuNtgrTcfbF4yttctp1p4Kr9MqvcuV1pbaxlgPaQ8mLzT\nMxV4Hcw8gRJ3lpBmPTB8N8nlwdbL9DK1abOXVbAmaaO4RSHptElS7MpuzzUq\nmlvl0xFBtt0H7UiDj6ZoAF5sWM6VOIdz1hf+4nePeEWJyrZOkVe48LmJhIai\nMAUDe0QNszPMhgn+Nq2qhTD/+nSSSrO8sYJ0U5yYjie/rDfMXBapj8VzyZ2c\nf78shMhPyYJWU0S1B3HIau6LTSlEHV2Dae9G64LM5XBFU9sJkyMBRh+ZnqPe\nJ7YZ3Mhq5/DgygeaexY0afm3N1YiDdNJSkPnO+kzFFzUznwf6Pd2tE2IBK6K\neMykxlTi8IGebOq8AE+ZMFepXHPh+u9RQwNYgUKOQdlnPyei41H3wNQ6Wn+/\ndNxK0HXv3/45YgbhcJcP/RLMwqIxd1MiXb5If05ZeRd+KfcXuEGNmCnzHvQB\n3vbsVOLmNmEWHQ8CpvWjwPyfkUbOz1ir2bkULu7rQ9tQfkraMPGj8etMJ/ap\nKpwwRrOGJEbPh09DGXXV7JwwGRIqGXbBVqZZFTXiitE0XXOVmCeIeX8mCVMM\nNsJjflskPSghhvF3x3pe7F0yZZLC+hQUFS8CiUp3A5qke0IvLlkWw59ARSRT\n8U13\r\n=LIQD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEUIDDSQdjrwxkE7lOMay2U3oGtSCuviWHguRjQ59qPkAiARbSM4EDAoK95VYqzdKu4AKhYaTOacFa/1BWBJ+qiXdA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.11.2_1640076206368_0.5212259616677699"},"_hasShrinkwrap":false},"4.12.0":{"name":"@sberdevices/assistant-client","version":"4.12.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f4b2f2240b87881a04c19d846a8aeda69bd4e5b6","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.12.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Rg9F5l/wDocbN2RImsBBCq+u9MemK79PinEKQ8L+ESUSga7lfgrMZH3kXTqapWqB6jQs7oLDho/3eDczNhGYhg==","shasum":"eb6045456b45e7c39afe64b0e94422d3c38d6fb4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.12.0.tgz","fileCount":107,"unpackedSize":2066362,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhwZ2yCRA9TVsSAnZWagAAYSMP/2BQ8d8VNJ7HI6KHl2J+\nX+UkgxgNJW7U9WBUoByGhUv1qebXRJMbjy1v4F6xWUvhWcZAyOMKd6Vj4eXf\ntUlZ0rZ8jsnIPL3MdPKbWJ/3Z68IEQmrUwKSOjsmQLx8ncZxinMGbkO4ULst\n3hzVfQ6ybusCfVzgUGOmdHC3RSYTOL8dbCvLgJYxzTd0ex18AYCHEkKXXnaF\n6ECkiWvtAZFOhnHe7sYXdfcGL/NQGKJMGqnhKT6bOafRqsv/N4rAR5vird4B\nmyEeI5OTcasoY3Xh0JhHey86qz9mcFjRU2DoqqSj/Va6874YQYsGbzteA69F\nKp4WyII496h3I5qPeuXV3xG/rNQQcipwICxLGsBvikRAQ8tUD+2XrMuHWfAD\nWrKQfLtl6pRMPPzgHXUHcjgvlxq9G4TkjvwmfoNPqDrLyd+WzzCWF7xvuQ5h\n13wxXKBV5EbkgOvmwFbtLumyWr/stPFR6ndGqEOPJqQLB2/2lKsDviObL7hA\nNEB8HfpbD0gt5DB8Jh+V2m1ld9Fc3WZ9Z48nLNULxdRgLYo1ps2kKpUkdguj\nhUTKUTkr633gJHymJ3XtzcpDGMPR0Qf5l3WJylR/m4TBi18zJ+k3+XKrzpKe\n2qsJ+jrmzs2yRSbg+HweIPbsEW+wrgpmf4qGSzd+78nqPZ/CZq2lVhwS138o\nSkm7\r\n=Y/Rw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHZ/oElj87q9QNd3L7J36eStn3DsGSjaUb0FY99Om35dAiARm7K90JpUpbOjK2+QtA0xk3Dony6zqqza4N9pBdg2JA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.12.0_1640078769899_0.14350660791123504"},"_hasShrinkwrap":false},"4.13.0--canary.216.9767b59a5ddf38e70185bd566b7062ff88ce8f1c.0":{"name":"@sberdevices/assistant-client","version":"4.13.0--canary.216.9767b59a5ddf38e70185bd566b7062ff88ce8f1c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9767b59a5ddf38e70185bd566b7062ff88ce8f1c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.13.0--canary.216.9767b59a5ddf38e70185bd566b7062ff88ce8f1c.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-so5TVpd3js85bMPeHjgckb0CVETrf+4DpSNhZJ4as1hr748itJO0Imh4OHGi82eDvQMtn7YqfH8MDCd+tq1pOQ==","shasum":"d96b3acaa3bfc1c1708a381c1867fb0c84a7ab95","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.13.0--canary.216.9767b59a5ddf38e70185bd566b7062ff88ce8f1c.0.tgz","fileCount":107,"unpackedSize":2068019,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhwZ6VCRA9TVsSAnZWagAAJHkP/jzXld1uFizpwHoHf2kp\nbU7wPZDq+PVrnySJGF0pK1MwwQW9fyReNPzeF1vcN6pdGN7vUcJZr4jh/a7g\nt/jKRdWTASCbh0L4Fvm4Io9hSQ8PXqqx+mVvjv324Tf2Z/sz/8ic1lTU7mRD\nTne+JVb7mJlyP1UOPyVLs+UPBufebHQypZZNNAckij2Bc0jDcEc+e7W5IgnW\nJiOXKqrJBZ/AYkCz52x99d3+s5DJUH2CUpaJgHrZWtxPiyhYxh0cXC0aNETI\nULqt+1cVHIcK8zv/q89QauucTElqiSDUPh8VtvEHnrBYONHWWC1Ksss5uBkA\nMfqOrCibKNXXBnRgep15PfDVS/6vRzB40gHDx6dl624+nix7Y94fVJn8gcOH\n6zPxNizbfrSSv8fwqd1H7p/alqOcJ6j2Wc/jPpQtqwWl+AvJNXtUOZftT4xE\n1OzhJsTpw+gzMGgN96iRPlCX4EAwzIsg13JztMsE+YpkD/nBz0MwKHQbpl6Q\nMYMIg6SjQ/pGx0gtplUx1Es4XuaiiFj/lS3xsz2t7X/Tgw/QGpoeJ32ZX5lG\nyr9ntIqyAxGCNUzsDro8e79lfK1mknwjK0tSYay4mW12vnsDQE0VqKRCTwYf\ncUSwyuuHPscYIaEivbfkAU/wP98D5CWG+C9wisyIaYnrRm/Ntxm+xQwNngPG\nwZX8\r\n=hHwe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID8NS5h0iQPOdVCMioxv0ossvKfJQ3wvfz58qv4FrZSEAiB1fV2ov6+2cY7NX37R2nC1a3qNut8tccDfrEbjhimB3w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.13.0--canary.216.9767b59a5ddf38e70185bd566b7062ff88ce8f1c.0_1640078997405_0.5906204985817944"},"_hasShrinkwrap":false},"4.12.1--canary.242.f3da956d84efd7c9a9121f3c8fc2a3be44cd4943.0":{"name":"@sberdevices/assistant-client","version":"4.12.1--canary.242.f3da956d84efd7c9a9121f3c8fc2a3be44cd4943.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f3da956d84efd7c9a9121f3c8fc2a3be44cd4943","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.12.1--canary.242.f3da956d84efd7c9a9121f3c8fc2a3be44cd4943.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-CThTfGCYneJG/fW2TdZr8HonwRZQBcFLWBI//+VILypPDUsI8bz2sfIReAdScvQOydzjjEPMC6noTinsE+QNDQ==","shasum":"bed6b2a6ef23e04abbc0e5f6a1299ef3eec8afe7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.12.1--canary.242.f3da956d84efd7c9a9121f3c8fc2a3be44cd4943.0.tgz","fileCount":107,"unpackedSize":2066699,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhwZ85CRA9TVsSAnZWagAAgEEP/iLM3kBY2H/Ui7djX02t\nZrgi36vQopEcLhr83DHPrbPn/4vklulhHQWX412b0hb4LDPcvT1jOIuGaWC6\nCGL8wOEDWK4ZrQPDF96lzuIXcPU3NdiUbZ+qV1OGVe4+WFXNCex03saiU444\nkOOZx267USr/2ol1TMJKpHp1r/doTApyyd0k2mFPJ2oZIkrJmxSDnAnoJQqV\njw9iPy6k8QZv8OeNPBgvuAazGJdJPUT7cdVN4+FVG+KaVoWgE3IhgjzDlE3/\nec0iIRsGMMUwfUbThHsvnRAKdef6UBWSEo6q54IimxIDvyzqVHLUB2acRfui\nzMOFqYJIiejVL+8wR37hXHRIsVhWIEOmgxOmpIWkuJVbgbtS0V+NFc+9f0wg\nySOWcCSZdI/U/xyAf9GmolzKrr7a1fM2sduuD2vsiS6vvTsyc79jkN2QvNsN\nYf9hTvaFWun4fYHkiFwezPYwD3RRnShkKhae2sqoU5Fhl32VX5TiGGP3EPG6\nIEBQ5Imu5gkJDWRoqcTuLbnB5703CFWa4RXkYmhROiTP2GBrnOsu6gcUPaHP\nLWfBdk55yVGjpOVODq1BbT2GYzydWhCRS8wR/9OsfIU+7KrTXyZ97n7FXKtE\neBqoRe3/tG/BOzobWDf31GKIxQTFLdPSqVrcdiqtj035LpoiqcVxkUWeNFza\nXo+u\r\n=3Tmo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCuNhnzIuzoZ2XqOurVgiqNQtou64nJe2f5I8MKO3/DewIhAIQRcJpa8VoRQjDPZdNgyeFuPa6drzqq2Jjk4DY3PUIn"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.12.1--canary.242.f3da956d84efd7c9a9121f3c8fc2a3be44cd4943.0_1640079160774_0.23701817818796944"},"_hasShrinkwrap":false},"4.13.0":{"name":"@sberdevices/assistant-client","version":"4.13.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"dc922bba5109ac75d028f8991662f1eae82e755f","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.13.0","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-rnLjKq/nKdtGJ3a0qf+eLVEUUi4i+07zP0nd0h6K/261P8gLMciYJ7H/6i/QIwhyHC5yV9x04r7QLjnUENYkMw==","shasum":"5c17763ef6ad154f6255c44b07c6d473c160a95b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.13.0.tgz","fileCount":107,"unpackedSize":2068180,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhwZ93CRA9TVsSAnZWagAAdI0QAJlWYWc/RY1zmqKzUC7w\ndpHRYM+GseuueuScagnDj31h2WMcG0NSa+Pyq0iCe3d7eyRhibF/Ep2CVbtf\nWsRIqjIxuMr9vjDLqbe2bxJDK5K9eIN8pfl2B3WLxZ6iG+UMpr4Gi3zMroSk\npQyOFk+6MEnY9y+Ykgecs+8KVMw5yYfVq7l3i3Ntg5M/1Y+Eay7lSvbxpoa5\nBHGLRbyO0y5IXULJe2Un4LzmsODjmvY52FS9fTYwW6WecmW2P+WCc8egghTc\n5bkGKOJwfe0GemJfdT52CIgHsPHzRFt43K9gF8fQi9hckZV2u4gNgiFdUaVU\nwq1PAXAMZc6O36AdiSHsjb2knRDWXmOjsfzGTk2YzXVc2hgR0iVWvr6U20gS\nJ/AOz3NsSbedCdGXuiQroev4/Qx1a4oERmYrrrTarGA95Cb/VfDyhYp+OZsI\nQDdOadyVSXMsEs53gWLeWAA6tviG3/MCtXRi8neQcBsgfqms3HOtWVzftnUA\nwai0PKOxmh/nz2y1MmlO/Bf280XpN0hdiA7uya/x9eKXtEUbQBrUGH4v8/2J\nwkgPB50VmEdUmueaA+iPGtC6+VwAP7j9lbmqF6QB66QghxEHwy7V09tTbg4R\nIsCV7EWdPcFVB1gfruGE/KnHy0I2hiv1MSxA2qju0Zidql9UKKtnMuJ7DMkI\nzn8u\r\n=Cnt4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA8v1n8AZrzwzISBJUHZltq6ROUy3pjHXvxzMDGKNJ/XAiBwr9uBlu5JpRCK7L2OTOfno6lD/uou7XaL1RWCw2wkEg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.13.0_1640079223455_0.9009415117721788"},"_hasShrinkwrap":false},"4.13.1":{"name":"@sberdevices/assistant-client","version":"4.13.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a7a4a23dda6147c2920e94838ee5884afb46e203","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.13.1","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-CP/0dgt+Z1vY/VuKFv3kOQWaxE2N/oqNY0Ewpl9EYxcH80fN8SElzhwD/bMQaUyBfjpWHasMJloUN1xEeQUJug==","shasum":"048dcb66a4c4bd97c0ed5e6b43499cd29a0596f8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.13.1.tgz","fileCount":107,"unpackedSize":2068619,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhwaTZCRA9TVsSAnZWagAA0JwQAIozqtZq2mUZQyL/9YWa\nj+Y5CkzDfGyc7Qz8hWrdS0iGnEfY3HdyLdJ3MMcwtU8Vao2WlL+N2mslntBp\nuU+h2RLq1cZk7RA59KvULN34m9Q7c+B4n196JIySrdgYFyopf82lK2lLPGvE\n+ODVNIzdVHlE1+wkr/PbX7uf306Ax69uLSNiq2sZIS0hNKvWEoe5ZjP4Y4YE\nG/Ev0vaKUZX2C1Kze5XgKTE1TXE3pkRJUkumBRKfWREoU9Sre9pAmwf9pDON\nnbFfkIORFgEJ0lo8LNHXQINx6LfDoTHMdNd92jjsVnMJE5wFPffY7CLQWtlJ\nq/d0p+hiAcvbPCuy9wrHxJJFXJb/Ii6NthlqDl9a8Di7MDWdFBaBXlcWeLTD\n870T03jKOYOM2ra5kb4b9lOc8pCjHCMEAuVRgKVucmaVtkEhAQeHZ4FYwz0k\nCLgJa0V/2SWXCiT4cUkqgdhGOzNVVeQAuT0vbVXA5B2jtghmtVo3Nc7SjJ5u\nXc9reSJD78VomjcD5lzo2EqzgH+5gliAqR02c7v7ur6LK6EcCoujzc+Rqr1B\n0o5BEE7U/0sbX2DPTcZfRDLoxcggJaffx++e/w1EryIvRcQkzld01HLOHpiz\nY1c0nFuMuIV0xtBeGrG777yu03xeXc8MEkm8fg8icRX0ZAV/9e+zs0kpgh5Z\ndH58\r\n=powK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFe/QpqrVqS9P4lftaJ0JojgM8NCin6tFFBqLPQ7NTbQIhAL4eTNk+1uB56lcEPCdETKESNK1XpjNoIbUnLm6Jv8MZ"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.13.1_1640080601583_0.13992140517446283"},"_hasShrinkwrap":false},"4.14.0--canary.248.7868ebf44498210539f2c9dbd8e3d01bfd821aa2.0":{"name":"@sberdevices/assistant-client","version":"4.14.0--canary.248.7868ebf44498210539f2c9dbd8e3d01bfd821aa2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"7868ebf44498210539f2c9dbd8e3d01bfd821aa2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.14.0--canary.248.7868ebf44498210539f2c9dbd8e3d01bfd821aa2.0","_nodeVersion":"12.22.8","_npmVersion":"6.14.15","dist":{"integrity":"sha512-dNmaAC5N74vgj2DRx/hwUbrlQB0UgRGVOmgPbErym7sE4VfjKF9LUXE3otuirhtiqmKqFejAkazio7GbIYmJOQ==","shasum":"d7448051f2f12f8ff2fbdb0beffc182ec3ea4bb6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.14.0--canary.248.7868ebf44498210539f2c9dbd8e3d01bfd821aa2.0.tgz","fileCount":107,"unpackedSize":2069636,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhxCK7CRA9TVsSAnZWagAARzAQAJkc4A9iCXO5FyaF6oJr\n7nl7B3TwDjpZcHPgfwoZMjhCattVjNqxvTQu2lpmZfQ8TaAtrpN4VtE8B4aj\ntB40+ghDABRMDFeRS/sUSYibGpGxfU07jwnP5fFOm59R18T50SXtjevbnVCQ\nPwq3ocypIgQv8UM2hepgM73VVf2LedUd2mo7YYtd9P+uhjIcns7ZGXBmW0kz\nO+A9UQ0jYzRr10ewDzNKplHTMg8/RdNCJRewnEu061X1q6fa0MdXOlsXZSNy\neGaHQkIe67h4Z3vi6sXkiT23NGTTjaNcCgfYqc4Ws1u8bJJBmmOyCYhi2yeR\nczPGEQ4Y+HcGytkV17zYwJHDDjl6Ng3zBTmhpIpxcEYAQF68XwME90H27C6q\nno73QhxfGtC586ODaKVTgVOnjAV7Ax8Jbd7H35J6GMdw7h7oqkn46bT7YheA\nKrA9oSTi2gfd38NtpDCfp+wv6v+LTWSwt3Iqjk+ZdtcVeOQAgr6rbZDp5rIE\nk+GCs5WpR+iJBqe0dOiWYF9S42SWosQ+MfHTHVh5UdRA1OamcSXt0nQlhx58\nibiA1HHPX1OW8Sxdzu8l+dZWZB2jJOIAqO51EEW+jwFLcl+7fqrE+D+IPJsG\nk/2JufReciIeBEfvc4ieL+FuhiUktgLASdp/IQBK3TiiR/4LYvca7PedYFfi\nf+US\r\n=cAjH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDYjtFOENvA45KfAKbkakEDUuCL03AIhQNEUgftnlSGGQIhAIiHLZqBWRghJlw6RvUlro37WlDrtVXsZXfRaAQP+6KF"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.14.0--canary.248.7868ebf44498210539f2c9dbd8e3d01bfd821aa2.0_1640243899010_0.6903826369615711"},"_hasShrinkwrap":false},"4.14.0":{"name":"@sberdevices/assistant-client","version":"4.14.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ac9e02400670014b8aa55d5d7ca6d902d7b7091f","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.14.0","_nodeVersion":"12.22.8","_npmVersion":"6.14.15","dist":{"integrity":"sha512-tF/xLVQfRagrQwb0hVuO9qFlqhoyWpoifzTOQSaYV7VnmEa00Au2LA8+Kq5irQm/Cc1UN5Ta4i/ZbHcpT/wKXQ==","shasum":"faeae54791305aaeaf5a17aa45961660d4d13d79","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.14.0.tgz","fileCount":107,"unpackedSize":2069714,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhxC89CRA9TVsSAnZWagAAC7QP/2c/XwUMD5UNQbWzgWvu\nxP8dJhYpc3bFAbAIZXDdh7TAZlJgHVvIxaXI0WMcPrgCeKWsdFUC7iN9SKJM\n0hritBl0z+UR9QW9Cq2kkhvzzOMoVQLNmxIhBwqrrNLfCg4YFUVMYki138g0\nZiOMTCBczFpEEc10U35TyvjRdpe4VGEelljlLo0J88zuZ0VR/Tx5coMb9VdI\nPWjMZFWqEjCyI8sIL6dhEQ2qdZ06Ks4GFQZdfDDR/gb5lTCwVnRehwsUuV+D\nSUHxnhkup+Fduz3FRmjdM0g4M9LN0gS5NZtmknuphducNSTcxmPNiyJf0fg+\nWVyfzfeSTxwD+or+Lj8CImPgojURfTy5851EpKHpe7qk+nxeJa5SY95ViASv\nMF3DenoC0TLUbjsETJNpv4/QjJKdahYIH8rv5yJx298fAnDY9UUpkhbJcuMu\nhuLTpM2T0vOKZlARySEFIt1Vd8KRoK6LiKgsaACQCYYkCsrmaSZoftGyDV8C\nsXbebEVnC9ZiW+UmC0MC6rzJzS04gv7E8jbQQ5E3ROsj3vvkmxuDPfWKmj3h\nkN0NP9vt2VtvKMxJExcVlRA/1lwuGiWaSvX2GMT4Lp6ZLH1ep+RSGX6agMvF\niP6B/pJtKEIqd8/yneKtzpGKwxLRwPlwm8Qg+ntIKyeOAfAYC0MU/Ij02Ilq\n7GoN\r\n=//CX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBbtr1OHJQphdz9nNNFckzlZULM8eAkxryRjoK3Z4KUbAiEAtLbRwR36Lt+zGFf7UwUBtXcnjIgq5bke139rdee7GiY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.14.0_1640247101538_0.46472885287658316"},"_hasShrinkwrap":false},"4.14.1--canary.249.e82cf1710f2fc81f7a96ec6611fc39ed0295246b.0":{"name":"@sberdevices/assistant-client","version":"4.14.1--canary.249.e82cf1710f2fc81f7a96ec6611fc39ed0295246b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e82cf1710f2fc81f7a96ec6611fc39ed0295246b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартаппу текущую тему платформы (темная или светлая). По-умолчанию, нужно использовать темную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.14.1--canary.249.e82cf1710f2fc81f7a96ec6611fc39ed0295246b.0","_nodeVersion":"12.22.8","_npmVersion":"6.14.15","dist":{"integrity":"sha512-QaAkTTz57NrJ/+LSoAm99uMoyGRb+ThQScM38q2VdMPv/FZ07Z9Yer83TBr+B82RKebd6xI9g305kbx3dOpsjg==","shasum":"692bb41484e2e064af30f9cbadc71d9aac7ce93f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.14.1--canary.249.e82cf1710f2fc81f7a96ec6611fc39ed0295246b.0.tgz","fileCount":107,"unpackedSize":2070405,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh2+9QCRA9TVsSAnZWagAA/UsP/16pi/goG099/rDF1wJP\nvQY6lm/KIwGeWelz5uB3QHLzUE9Fa2z+J3l9/Vdknf068YACWLDxE/iz/dyB\nUV82aFtlQIvb99iG+uakHYMHZVEgFi5yRB8QoUrxsORD3LjSiKStTJ+LrxBA\nSHJ7YLpQiyfZRHGAUoEoAiIRis63nfQ5IigB3QI4O9EszEEC9b78Lps/MvD0\nFyKOXljR6NrjF+NHjfZTymPj3fyIqAn1uV54P/sH3iwxl32EyNeiOaaljRqu\nHnJWc20EjFiVcELwBAhBaZd07nvbeRBD1v7wcCFlyaNB8f9yRmxaBv4NeIUs\nsgJrLb5rw6HYmeX6YTCtprGlofubuo6TigQYKyZsNnQZ9yU/WMHeBy4Q0hjP\n2mG3BXouqXELBpq4kTBd2XNF9V+KpHWTmYjraeER9N1P8o40T8H3BkzmSpVd\nqJN2sbFHi4vkfeKBIL3zEXQ7c0vsKeLQ/ZLwhsRn3WOx3IBD41AAtEdHvB1t\nos6paktZhTEmzMgB074RtPGHlafSTU+kXymeBr0WQpnAWP/KDfhm2j4Lrdnq\n44/Hfrihh5saqObX8+YFJWXt0hU3yUQvtnEbAQr4sRoUs6zpfq0qbT683cDu\nGQbzVze+KVdyQNsZXrsMiCb3rzphmvK0sPOl2FSqesLQIN/gn1IoRIjGf151\nF1TW\r\n=vqq4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBihbEeF+3NT/oI9p19pZYVmKu6NHntJfD3nPmQi8tx4AiEAtr0VE0JHubSEB2Y9Jw1jkn+mJIog1JKV86MPw5HPmxo="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.14.1--canary.249.e82cf1710f2fc81f7a96ec6611fc39ed0295246b.0_1641803600283_0.4039189184371137"},"_hasShrinkwrap":false},"4.14.1--canary.250.bfb9b464275221f9b5d0e158358c7c6486151fe4.0":{"name":"@sberdevices/assistant-client","version":"4.14.1--canary.250.bfb9b464275221f9b5d0e158358c7c6486151fe4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"bfb9b464275221f9b5d0e158358c7c6486151fe4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.14.1--canary.250.bfb9b464275221f9b5d0e158358c7c6486151fe4.0","_nodeVersion":"12.22.8","_npmVersion":"6.14.15","dist":{"integrity":"sha512-r5tPBE8ppm1YuBGbbFVJH0iq1XADvYdZEoK5FOQgveVLManXsrq90PtwBo6XeW7hJu9VRKMik1tZAlKCNLZH6g==","shasum":"f634d41174ba4c84b25756ac73dc269dc3585cef","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.14.1--canary.250.bfb9b464275221f9b5d0e158358c7c6486151fe4.0.tgz","fileCount":107,"unpackedSize":2069934,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh3BeECRA9TVsSAnZWagAARf0P/3N3BVXiZi+tD1Rkd5ZT\nBpG9vQL6+meJonS9fwhCn3DAakc/K1TfT/hfhwn4JGDs3cq1Grp8zfiaJ5iu\nDR54CK/2hehFQ5xqtPxyhaCsZuyjYO0y7NBl9B89dogeFeFOZxOq4RjFFjnC\nPnLuuW4491jfrGURrYVnvLxTJYtPH4xzrRyv8JvYWbOnJfktCGs8RPJ5Kq9v\nF+pZKZgUXtRkLN3z3ELU/5kBkTCaoSsveJ0eEUxRfrWThq0A+LFUKqeWNwNG\nbmt8napslcf6utaSq4I9ctMqqlu3PD5j33asTmW/rRI0HtGbaVQ/B50g79+E\nQsF1558Huk8nRHx9RvrdFPaS45uc+NGSLKAPhRbAtiHNaBQFqt8T61AGgR5+\naz3ugGHfW3UCxOhGSPOXnNEa/72nJDFQOR2zYheu4O/OTUWWqLhLgYXHkzr7\nnkPn8jPtAZbiIxrBuHis2da+WjjNI2aD7Xcm270dsAfY9lSo61uagdcrQBYK\n56dsG5kChMjD3pD7oxFTHx01NH7LgvyWvE7HKy6oKHdgyU4+I5nbcKu1GpTi\nGUWq8UQhGFDCNsv9O3SqIXmuW9oyZMemAqJ6kyGXXD+gQPU2/VrUpZTfFGlg\njkdo1rQY+8dAfXHW04uqUSiCwktyRM8fu9+gnfS1ZMRwCOC9tkfuVOk6FPsh\ntIVb\r\n=GQE6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAeuitZyJMio0Qw0FWopCikW47/+5D77UPomfc3HuXQ1AiEA/as7LQyQduWQRPuXiZZhuR1Fb+lnGh78YIhYba+ztFk="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.14.1--canary.250.bfb9b464275221f9b5d0e158358c7c6486151fe4.0_1641813892371_0.7379548976517545"},"_hasShrinkwrap":false},"4.14.1--canary.249.400c5f2809b2cf3d93dd11f76be47425146ca6c9.0":{"name":"@sberdevices/assistant-client","version":"4.14.1--canary.249.400c5f2809b2cf3d93dd11f76be47425146ca6c9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"400c5f2809b2cf3d93dd11f76be47425146ca6c9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand] (#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.14.1--canary.249.400c5f2809b2cf3d93dd11f76be47425146ca6c9.0","_nodeVersion":"12.22.8","_npmVersion":"6.14.15","dist":{"integrity":"sha512-ckOzUnJROBaPtdykpKYB7vBqL41UkDHNFiwnm41pmDQUTSd7ewD452r1Zu4K87gHcwMpW20lE3o7u1/gv0BqdQ==","shasum":"800c767b202ea87b6567072deb8891b2307a36af","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.14.1--canary.249.400c5f2809b2cf3d93dd11f76be47425146ca6c9.0.tgz","fileCount":107,"unpackedSize":2070460,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh3B3FCRA9TVsSAnZWagAATVEP/insHlpqakntYAQhSXdG\noehNkCsbprtEACQ0W9fDOGVMBs+oLsU1q/TkNvNqVRzWMHyvSgtj6YxSkVBO\nWZVPTRKYAWJNa4aFl9l/c8pEeEywR/jFnt9BlJqVBT4Nw5uxmjExxglhSBH8\nwKKUSC/OIjkMQDTmeOsnLTQB6aU2C8YoR6/9IR5BSpnXd8oxb4tJcs/o49Dd\nNJ4uxD5psQXZOFS4cALc+DjRDvBHao2pqKcq8f2mkOCAY1lJl+zcsDMTHGOt\nuEL9EgmBRGA9O6BVswZVFctTLtQWsGG7n8yFXkv9sCQUVDSm5vmxoty+mW1g\ne6RvNR98BNZteJ9eo1MbWjXDHzM0bKv5qmz31GB9I+7oCayG428pgpFt3S4t\nPXfNat70HgQSy29JQ45uqrS5jpvWO0zZ0TWgxYEkoMyI8BI3JZwwMAkYHy9l\n9+c+1baI8Zl18WSmjja5ykvhV+b1+whDFaWoyj9is6ZI7y3hOGI8cdW+OQc9\nN2dHnqaxzXrupn97AIWyJ/su0t4xEUcSYPrL+KuH29rJcja4EEOVIMcq/YOj\n/Y2bxTN1YHDQH+ua4SWg1EEAQ+lXR2BknSaAFGQVLPryJRsDlBqfpAzastoJ\neCYCfVSxVmf5sk5ddlxea5MksCkb8sGqKhzu4ctHIfaxlkyHIJk6KJYxyq0q\nyuKo\r\n=p53j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD6dDlxgI9qTmud8FGcRskIT2DqlWltmADPgs8LMx1cGwIgb6FkJWtAzqxAHTr/nfg1D24xpbO50AgokH6lxebwd20="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.14.1--canary.249.400c5f2809b2cf3d93dd11f76be47425146ca6c9.0_1641815493767_0.055424611436174054"},"_hasShrinkwrap":false},"4.14.1":{"name":"@sberdevices/assistant-client","version":"4.14.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"fa6bd0518693a7df0241098a7b6ff365c93fd681","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.14.1","_nodeVersion":"12.22.8","_npmVersion":"6.14.15","dist":{"integrity":"sha512-pG1Fow2a2ma8PsI8xVxNqsFdoaGtAsMKFnWFtqstG+UNegcWZqmiDY0z5Enxx3iYZFkFT1amrGlGq+adTtL79w==","shasum":"dc6e0b68ff5207fed54fdd0c18469f2132f01993","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.14.1.tgz","fileCount":107,"unpackedSize":2070719,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh3CBzCRA9TVsSAnZWagAAeeEP+QF9fX+mJzNZDcAV6AKu\nszkLIoWxD++xv98CcRkeg+0OBF025eN3H7pXhjB7E1Yb/zp83ZonclOwij47\nzO3ZzoNqPOcgayEWEYPhXM2QTNeK0Q4fijnRZxzIQjJ7ZdWgIbZ6E68qnlKl\n2IqoXwrQC9GYzrRFQAaLbyY67Ty9jaJQjxCl6enkRsL/7oG3uPM0chUWgUbx\nEBG0c24dYwWwojdIpwb8ekIGkpGppjuQjgZjgWNGK0AAwOcmmOpSOyJN3lHH\nvZDPQ1Ed8RX/koiO97cE/+UX6feEG9OdMdKteNdJn/9dI4l6QSuX4m6mBC6V\nEkcqNdtb8t28vPPboGpEym103IORMTm4KhdjeuZKA+AMyb7OE026L87rR4vI\n8F+4Les4MLdI5nqrRNpMFw8cpQPTAqf70tdf1VeTq6BWu2pWSO2WHfmrNONq\njjpoRukCOKcyQGSezPTx7deH2Ul9W2ico4qgbe/MzifhGrb2ppp9sIf000PA\nCNCENVuI0NuI7zN1MYIxEbkazAdpJxODe39ywYOqI5iPS1KMILgP9bNLfAHB\nwthgzk5lk8cXyGaekkXr8QZ0htr/Mtl8OQ1VYXTSSVPDE/6fiijdIjceuShO\nd3Mli8cQGsgVJxJMXMMyRgKXswZytGJjkNnBzivYa3ln7/YU1yPb5d+rNtk6\nQYfb\r\n=c+jv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCGyZkKlgXczy7JylxLt6+i9eSOXJcqTyY3FSpaYrdWwIgBRDrBrl6doz+bcpCcc35kuX66s0TkyUgsmjNy7J+1Gs="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.14.1_1641816179401_0.7595065545899782"},"_hasShrinkwrap":false},"4.15.0--canary.251.cb8d48a2fd642ce383c930d67c62e8422123973c.0":{"name":"@sberdevices/assistant-client","version":"4.15.0--canary.251.cb8d48a2fd642ce383c930d67c62e8422123973c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cb8d48a2fd642ce383c930d67c62e8422123973c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand] (#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.15.0--canary.251.cb8d48a2fd642ce383c930d67c62e8422123973c.0","_nodeVersion":"12.22.8","_npmVersion":"6.14.15","dist":{"integrity":"sha512-byK1FLoHNDujMBU3VSl4yGQDgB5rudyi/XtxMarABVy7dVXC09VuncGvfm9I4QRVO9vMIzgBLX7cBmw1BW0NgQ==","shasum":"a07c635d2ddc47f1e8abad22e52f30bc2ea4412e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.15.0--canary.251.cb8d48a2fd642ce383c930d67c62e8422123973c.0.tgz","fileCount":107,"unpackedSize":2071461,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh3C0tCRA9TVsSAnZWagAA4LQP/02Vem8I2bue2bHwCMEB\nf9zmXfoU8bU4dC0BBGCaUtBQS4WVr8OEOh196c9JjRVcjOZdkuZeMa1Rn1ov\nUOXeWGBR7cI+ZA8uk6ljlp9ldz3guX3z/yqppWg+M9dhg1Xf6gVoOeBUzQ/v\nxZ7x6Ncxzpwi+EoHf879A8wN38x6RWV5p89GuYlGIxcalPR5yPuvyW5mXd2Z\nI1XgBYfmxW3+d21Um7ESxL4kQm7C9g61QFRrYA6d6JSuAGoH0g4K8YSueD0P\nGE8U1nxj67q/J1bXVFq6PaaR3iNAUQhxXKioBpWexNLPEmHuKdp9Feg1WQ0J\n27qc5bCyXUUxPRppnQVeYhqcfqha4PILM54l1Iv10ctxImWgMXNqBajf8PFt\nLmXo4xQDq7Ml/dIbuXSipE3UnU1nhsstLGSqNQYFGFWALirIT26rj/aeXsH8\na0cAtp6EVrQQDChBAksgbTT7kVOzzDqZymJAaHfgk0t6MzXqtwqSUMj7cGPC\n6CTqFxZyiw5XQskQvJeVNjbTfMk351e+UDYA26RoekDAt58Cq2t14XHC5pkx\ngMsSfPJ+bsnWb28qsDaECaOpwTd1Togx2fhU9QFGl2ZPjC+kK9CQzbaScW1C\nRXUt2DMudRHtdI05uZOfdthl6exLh67LMprwM3/TR/whfl2uyERXnEuIzm/z\nFnjm\r\n=TtS1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGFrK5bJSwlsVAMCHiRDdm5DcKlfOHL80Uvh1/3Bis9ZAiBDU+S32ptp4XxkrrMIxwMeWhk9dhOYvt6rPn+plD/s7w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.15.0--canary.251.cb8d48a2fd642ce383c930d67c62e8422123973c.0_1641819436925_0.13462689870411126"},"_hasShrinkwrap":false},"4.15.0--canary.252.dd7ce278844629b3f2938009bf5445e2af3ba565.0":{"name":"@sberdevices/assistant-client","version":"4.15.0--canary.252.dd7ce278844629b3f2938009bf5445e2af3ba565.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"dd7ce278844629b3f2938009bf5445e2af3ba565","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand] (#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.15.0--canary.252.dd7ce278844629b3f2938009bf5445e2af3ba565.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-yfN2z6gGUllr0fnEt/TARNxa6THkrlB/34yqi6VHAVuist8pWmDGtm+8UAUrXVyLsQ4N8I8L/tGvwO85wQfFPA==","shasum":"c96c643954fbb374208457e96de34f1a1343e5c8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.15.0--canary.252.dd7ce278844629b3f2938009bf5445e2af3ba565.0.tgz","fileCount":107,"unpackedSize":2073671,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh5X2pCRA9TVsSAnZWagAAYDIP/1h8J6dv4Yzty8Ye5bLJ\nt7QtwJ6/NVpcvdo/F7u+7KdEGNhiAcXifxRi4HBnj0dvcKQMFBzo98BDH6QV\nVC9sV7oXIkUB48dDE3YrmdzM0eh8NZBGWtmtmR+UVm99nP8T4M/Ap6r9VaEF\nFd8Gnv383C+fJYkpXVKq4RxOFO1DXNMkPbGEQaFBcJ0gIfAl05mpM+vcxLys\nlY0JfW+74LEaWtP/lzc+PG2lU+fZ0vevcyF8smQGOBPW2hWGd4HedbNakfDw\nt8cSPNTGZ46MHbL5zpOTXidBDE+h+jwZbjSUeoMVykCpEo2+UFeZMaZMQMmI\n0mIxmnA1Gm6Rwn1pxSvG782jsJr6zxaLEknKpHrXNdlT9U2tijM/ejaiCb/a\nKur36jLcLxcYg/b14XZkiXV55p2XWkGZg+IMsWmoO/fiYlYk52cnRK3w5aKb\nFM0SRRSCIueW8tFTIAvPIgHHhyC2wsx48FKweStM9gdLLBOkmC/AYnGoNBZV\nZRimS/akhUCgxVJr8BWuStfJgGbbwd73lCLmz1mr308G9D60t/yWELbco35r\n53kWiGN7NSOluXW08xxFNvXrwuo9P1tcVpwl2uvZ3PQFn8bcC/J0rknh0qQR\nOu/n7+JpRaUBpFqqa/DtYeqVpvi/JDwdp85KAI5ddiIwNPrBeg4IcsujOOoY\nUAdf\r\n=/NLl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGQO3BEVla6iBOMqYHT5Jqnl4jcLeuiF5awXcpx1BjfzAiAp8YviiVXcPdu39WCjGdjYx2UyNUVeovmxcS/dbXJrrw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.15.0--canary.252.dd7ce278844629b3f2938009bf5445e2af3ba565.0_1642429864900_0.17250086393923292"},"_hasShrinkwrap":false},"4.15.0":{"name":"@sberdevices/assistant-client","version":"4.15.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e9a7c937b35f947f18940211258b2cfef1549c4a","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.15.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-Hv4o7Xk3nD3LQPX+ckJoQHdPPVUy2779PvjH4WyZ0iAbsv6BipKJiPHaZfvHUKZQO4QlDfOVzUzk/zSxYs4Vrg==","shasum":"a54beef636407797bbd754f3b76b6eec4c6c3e95","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.15.0.tgz","fileCount":107,"unpackedSize":2073763,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh5YScCRA9TVsSAnZWagAAO6cP/jl9iPEhY6efDveX9Afu\nKW8zXb5nndSYEjYejyx96JptMkucT6OvpVx5xwGsY3nK6dtPH4LuEDINZKlg\nsIfBHo4RB/J6cEtOwJw8rd/H/SXs/ombwd93vzWWGqe0M2xUrwb14vcGDJup\nZSSqRV5qg6OSNMVp05iqzYJzm3svaHgyLQC+8QrNbHo6iW4hBkyWYOCE1Uoo\nRwZf/AH4ggET/T9A4Xt9856I4IQZQbxAwkAtD5e+PPzLBGS/Jch6eRQBIwSu\nid/Q9rKgbdGQoIcwdgjiqonkEQ6SSnhyr2etnrbE021IfJpdowYIsjTYSO/C\nO2YsHJhmAqi5gOmA7xXz+DiSs21IGo90lvGrHkdq2++TgKLIuPDvhbwO7mX3\nscVTBeg5jzj9UnLI/2XM50z3Tk3u64Qh2Q4ltKQxbB61me86XnLMAR7CdD48\n5D3QuF4dbubNU0aaefkL0i48zYQsW2vbuLcfBUlYVSqoYyEFWhi/ukLKR7Co\nccOuXWhTqPFY02MPHe5Xfv/gEs/TqOvsUFZrkZA3jEh6HUdi01ffuwf9pBIy\nYpj+0HIilNvAaMqKCA1y6X4OS5/HvhyTtW5YtI7Wv3YKWp5vuipY89hgxKgn\ncDPoeJKOM0aZOOqemcHl/koUyz3x9Bu6PJwR6Ukp+LIZWWSm5ehahCm18h8s\nGycs\r\n=znAJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDUqNF1GuXMCC672HrbdV42Lg0Z0uhtAEA2dNm/VnSvbAIgWHlMERVMAyAAi4pvIc3Vb5sqMD5Y0ZCHH9xctqVRG+4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.15.0_1642431644650_0.6993359392566325"},"_hasShrinkwrap":false},"4.15.1--canary.253.5e2f984bf9a73863e36a7090fb09500ae7728e1b.0":{"name":"@sberdevices/assistant-client","version":"4.15.1--canary.253.5e2f984bf9a73863e36a7090fb09500ae7728e1b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5e2f984bf9a73863e36a7090fb09500ae7728e1b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.15.1--canary.253.5e2f984bf9a73863e36a7090fb09500ae7728e1b.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-YRVU3sPX4gCkx9p09o2XtnWINPg5D6+PgrDfwcuE9hDrSlCMcF3PVgQHqRK57S9Npl9xfMMOHOLaBM+QAgK/FQ==","shasum":"05b29540d59a8ad2e81320860342baaa30355f13","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.15.1--canary.253.5e2f984bf9a73863e36a7090fb09500ae7728e1b.0.tgz","fileCount":107,"unpackedSize":2073982,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh5r1NCRA9TVsSAnZWagAAYPgP/29DURUZx9jeUt0h5Rl8\n/t5iILp335OJkYZjsK+CwnBJLN6awCRl/xXdQ/QBXpJX9Ze04bK0T6ldxhAn\n9Nc+dyQrGpBdum6A15eGgRNgJf9P2yneSHnU00cFmFp78rfmqNc5vEvwD0K1\nvYYqzf+aZ9unHcJkgTFcjKnKif1ZkL/tCYhZ7v/YUa2DX57xU01J/bW5DTsf\ncw+IgoyANKYBHm/SBLBOB8FsFzGCDSQVPMWg8bPr5f0otIUS6VlLue76jAOw\nqSPhvoGawXcS1qq2hcS1Uy4ld5amsJ9GZ9FxZwMugYTtDI+k6tuhdZzKwrlu\nnladm4a7FcGAs/+rsidQ2cMTkdLiQO2RWH376iHROpyCCCk9q290jWzaYVLX\nuQMzq/aSi1B7gBi++8Qo8D0MUx/y/D/V/wmAvclPSytXBSrUEs+7BG+JetAl\nW5L91UazVHwIDotd9VcaHvequzh1Hr7Vgghq9HfnrrSmIyteF2rebsBTNQUe\nRX41VP0+SePUzhVBIhxL8g82HU2JDucbPqdGw//9pEj71MfBJ11uqGRUuln0\nkwBmdKyzBgfeElof33AED3uC5rgr6/CfFmE7dVl0ePu/yhC2CC4HjnsD4L2w\n2I7An6/lZbjLcr0ZUFmiek18Hccm+cthekbeXo3i2wbqa4qSjkXGMJMftfyI\nd9hL\r\n=McXd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCAk/Ich+2PRFbzsu7vvRXcwYGmo9tCdt36xXXkpR7fcQIgAwACPSAmPwhsE9/t5xHineWu52i9FTsX0aIcvSiG9SA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.15.1--canary.253.5e2f984bf9a73863e36a7090fb09500ae7728e1b.0_1642511692995_0.17843499798730722"},"_hasShrinkwrap":false},"4.16.0--canary.254.849ed59a49164b3601aad9aa4bde933988877031.0":{"name":"@sberdevices/assistant-client","version":"4.16.0--canary.254.849ed59a49164b3601aad9aa4bde933988877031.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"849ed59a49164b3601aad9aa4bde933988877031","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, наименование поверхности. Возможные значения - `SBERBOX`, `SBERPORTAL`, `SBOL` (СберБанк Онлайн), `COMPANION` (Сбер Салют))  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.16.0--canary.254.849ed59a49164b3601aad9aa4bde933988877031.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-mPdJxPRxDjwh66KoMzOFRlLOhWvuWm1BVTD8xrhRrzdQWrMRpnsRwUEfLs1J/TJb0Kx0ksJDHhFE2uSl4idRrQ==","shasum":"870e300cebd150b2dd612781797a9b02169ffcd5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.16.0--canary.254.849ed59a49164b3601aad9aa4bde933988877031.0.tgz","fileCount":107,"unpackedSize":2075746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh6UYaCRA9TVsSAnZWagAAMSQP/09DZHtFsUiEorK5zuV2\nkFWy7U9K0aWr+nhXmvX2SslyMXXduFoFuV1ot768yeCqVa+Z9hmKm5buvgnj\nGqKRUFzp90s7nmD+4CbGPzwJDLmcQZZ7n1Htz7Yk5szUYXf6OzE71Mu/GrWb\nvMwkqg/zW3vIU70KFX/dY7g+naS8RXZ32EUEPO50qW32y1jQa52lwh3DyU1m\nkfICdMHhZD1c7msnTJK/eQNCGA/UeryF/WVx0LjaBExwt3bUAcBYffya3bdI\ns4oMC6V+zkmyfsdjE4K0ZyRFxOqY5xZFsD275sb/6wKWIiv3qlExQ60NMfKN\n6toZftmD3vBw6SdRvcqjOmyTzIbj7n4KzP9Orod4aItQ9rZVRuTm+HVIVLya\nNTwHMgD9rLgXChCaVv4oQmoDBZsc6AkLP0BhJ9qOMIevLTKd+d3j3cQ1DkOk\n9Owx05RfmJKs9UbHBO3F0r/l+RYaOrYrzUdHcMXqrp3MwNolqosUyCllNQVM\nInkGAAx2XphvJDcCUnLDmkoGrxRMvJJ17KBXO3x9ITHKnsmCktdlBfNLWduO\nGmuhxP+/SttzJ3SYu9iTM1h7KvKXy926vX9fs15vOGje0hOvae4Am+hW3gYZ\nZ6T8XNEUlajPYCQjOZ9hUPMeKI+UnUmzXiq6dKPeqKV2zlWblK/2ll8uiSAZ\n6sDL\r\n=/Sof\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDyRzF3zNVR/Bqob+kSR6fCaoJKncLpj4C5++A9ZjwuLwIgLNgRhcS267vT/vvI/fEWOwzZ0AVhm9WhwTJC2LvNPuU="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.16.0--canary.254.849ed59a49164b3601aad9aa4bde933988877031.0_1642677786124_0.13671933551030224"},"_hasShrinkwrap":false},"4.15.1--canary.255.a27267813272811dd31797b6a0dfdbb1d2f52bf3.0":{"name":"@sberdevices/assistant-client","version":"4.15.1--canary.255.a27267813272811dd31797b6a0dfdbb1d2f52bf3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a27267813272811dd31797b6a0dfdbb1d2f52bf3","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.15.1--canary.255.a27267813272811dd31797b6a0dfdbb1d2f52bf3.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-3fJa8BVZ8kXwskcq9i7OcibzJr0Cm7Tj+CYJc3PSqh1ub0xdZUzJmo7wBsA5WzlwrP1+GozivJhuRDlaHyolNQ==","shasum":"2ede9d650d58f595d140d956d4afd53d4b8fcd32","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.15.1--canary.255.a27267813272811dd31797b6a0dfdbb1d2f52bf3.0.tgz","fileCount":107,"unpackedSize":2074228,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh6UnfCRA9TVsSAnZWagAAdbkP/jhpFcdDgzWfmW/NnhpU\nYHJXiIsDTyHWOZT8WTpuEfRu0tioezVnIBFaOPKk2drpmAl+sevAY9+KzRhc\nJpKg16YrgAy0HbSdizpFb2NwM6pbHu3BslkQ3vWoI8O0BcXjQKwpaJpWJoTl\nMw23HWujtf4bC0AteE54dk34laNDApBRP90rnFhKyov0X1OxlhW3q5XtgGU1\nRvS6cl9Wvn7IfD3FrE7qrprDiwYwrCZPA85ElTCR11inYyHORsqtMzy0s5uE\nhFDo8OW9vXLYf0ARDVasHuPv/xBAhL0r4sqR+g3CraSqvfFQjjnPlyMEda1I\n5MA4H5nXrgXaeAIRMec7QpJws1Xsb98MX5pdvI2UEP+8mJJYokSNsBYL/VXW\nLbGnttOvNWTYnz+zMWUviji9uuJn5pQjlOaMcwVdhuvYMCr2KcaTiM1VIvpA\nS1zpgcZOkh54LoB6yufBgJhDrETZ3jXzvdbFzNWa/ytNYDyimal6DQ27WBZB\nsRuuo27BqiHl6M9PVLVjwUfR0ggMwh91223Fey3BgGbOo6hb3ibf3KKmXecd\n0PWva0fNjyM0OvX557hJCGjLzoj/45kMiGmKx3/CGCocBz/Yl4Hy46CznIw+\naqEiRbzRKZKczzPcmODhOG6Oxsc6DLR47kzcB9+3G91AkO2TF8Ns4Ox43qvT\nzgHc\r\n=IJGz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF637EOJ4RW9aLRFfATDU1e9SpiSvxdjpdddtnh9zTtbAiAcYkvrkyCans0GE2uAJOtApq5YTfS+KYqaWstDJrSBRg=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.15.1--canary.255.a27267813272811dd31797b6a0dfdbb1d2f52bf3.0_1642678751699_0.6203488292466144"},"_hasShrinkwrap":false},"4.16.0":{"name":"@sberdevices/assistant-client","version":"4.16.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"795c8ce3e11485f42d922d24667ad9447210eb6e","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.16.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-bdObLEwREg3GHEZBiP4DxPyex5MXUVp85WU3Pvh+CCmR4YYQqeIugF1UBOOxbU3lPImxvR15Y7SRY8gDNZPvPw==","shasum":"0f12242cf22e625a69c10d8a4ff63d640ed45267","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.16.0.tgz","fileCount":107,"unpackedSize":2074961,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh6UoKCRA9TVsSAnZWagAAKGIP/iRaaBbtdLNfB3V2dNOJ\nn99ysgtvd/8daEODmBLzVqwLddN0ex4kc7sYXU0zD7eJ6XBt+0vGaI+Zmcqu\nJnHna6agVekDufx75REsvEHAaB7Ng7Dp3PCYYuMMZLPfT1d8mUtfJb/HJa8g\nGBilGDfPwWLBZGEaWEgdw/RT25wxno/zKv7lYR2tGnpgV0g2JImVlYk2Se7b\ne2RlVu/MUMRSyLnuRLHfLhnYdvn2V1eFnulR/EMqFJzf+1Hd6VrOE+nWpiyw\ns9uToeRocsqgQxUtv0AAl+z56hTA/bBrTKeS9eQiuF0HZfXhowSGkiWgd7ak\nPV4JkJAzB44VFvfvNRn+sTK0v2jqdcEuiWd0fUN3E1TbKP/qyiijGmkpcUzk\nuSsql6vvFiolZAiv/rSw4k7KHIweUVGClrcOHaiVH0Bnqs2V+0WR7hut90ya\nCpvVgNMPt3+yHrmjgztk5IcMJ3q49ygPgdxOn5XGOAZfc6SKT0eXPxLdwLq9\nFs4gjkvHroPbhGvEuL4SECWe1I6lPqFmcyOjoCYz/To3EHQ+vFcz7HHiPqpp\naw/nVB7YrtiV4mWiR13/JiYZTdU9pfLMgUngmnX0xEsPxAjKpPripWmUu2HK\n33rTRgzUKmhTOc4WzbkfylrXQYBuYopPhnehe5PtnUxoJEtB/5rEcioSwPTd\nU2KC\r\n=ImK0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIARiIrYCexY8SldnPaS0uK9cTosXKU5pDLNMI/SM1NZZAiAVP0X0BFVIq9P2vVBrjHsQrRK6Ya+4FzhGNahvyqwERA=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.16.0_1642678793806_0.03091835978089197"},"_hasShrinkwrap":false},"4.17.0--canary.256.004c305de8dd0429be86cdeb3b7d18e7a7e5457e.0":{"name":"@sberdevices/assistant-client","version":"4.17.0--canary.256.004c305de8dd0429be86cdeb3b7d18e7a7e5457e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"004c305de8dd0429be86cdeb3b7d18e7a7e5457e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.0--canary.256.004c305de8dd0429be86cdeb3b7d18e7a7e5457e.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-UegV7vzMLqqnpaARBmYjS5bBB9JjBtrkX43RpcYPzpL7Xoj/wkE0GABTQZzlqinTHNnWUAceEztQtYzzTY2y4w==","shasum":"2525f086a469c0e4910789a3afbb2f9295a9dce8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.0--canary.256.004c305de8dd0429be86cdeb3b7d18e7a7e5457e.0.tgz","fileCount":107,"unpackedSize":2079250,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh79NiCRA9TVsSAnZWagAAK3UQAIexF9mTApsIUpm6Uu6n\nwcWh9LjV3v5lDQAEJ8zYXA5Db1rubGX7v64qy8msD4RSaFVS183bj09GQW+P\nw5+veFsd9vNC2AtAnP+FxFmUNLf7gQ1GadzJMzKPI2JARZEIKFqDAZB8tHFm\nc9k4uz7QsljkNSa9ZwDR8kYX7nVql7NzOUcf6Zw1OhCNwyGnK8AFRTGYiIxS\n6Cvg6vY2myQY+t7KmW68X4sRsDDpTqop+LWOXQshcKcAMwX4qcM2J2lCETCJ\ni8Zl3jIx8KwVYuSNyobGFxVBVV0DEv4N3Rw7YVAr1DW6Wp/dr96aVFEQoMaL\n5JXYECO9nIT3CfhU5keXWeey24tn6qq+iqAIyd+F8/08ouD4v4tyrqEnesiE\nOxt+Wo6ADpZarrNG/Vy1MW21mXMema6YOdNOV82zD/wynjfu0ARHAiY4XfpT\n1nIqIUfzzN2ERXXl6sua0GZ4Bdv3lvqSd85QZzZZlbaPCUrh7r7oPhrGbAV5\n8lrf8DDoEhshRwXNLv2myLqMaUXVxrVN+Zs0UUimwv96deAagZExn4DEJeY3\nS2kqYQToMyUtNjMLTIaiPXLfWVUdBnK2/iq4Q7MIrU8JlBgI3/oSRPUTuOMf\nNZJQzT5wITysn5WLZ6wSZFN/ZPUY/tjHvbDQ8Hw+PMRqpzpuSf9U0ZKZyWey\npN5I\r\n=FTAv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHOlJR34jH8nbRvXB//J4PqI47/cE6etl4D1GJ8JEQ/kAiEA9TVz+ZTGkXIxkZnGUpHKndErU72VM5nNkY8qtHGRIs8="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.0--canary.256.004c305de8dd0429be86cdeb3b7d18e7a7e5457e.0_1643107170364_0.7569288696570151"},"_hasShrinkwrap":false},"4.16.1--canary.257.f9cea8ce416d0871507b4c97eb971781a2531a5f.0":{"name":"@sberdevices/assistant-client","version":"4.16.1--canary.257.f9cea8ce416d0871507b4c97eb971781a2531a5f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0 || ^17.0.1","react-dom":">=16.8. || ^17.0.10"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f9cea8ce416d0871507b4c97eb971781a2531a5f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.16.1--canary.257.f9cea8ce416d0871507b4c97eb971781a2531a5f.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-ZJ65Y+f7EJ1gjAF6QOTemi8gtNKTJ0I7OCvkFWaiBpsOnqkeOvsVYvqKmCxOWemH/Wd+bzej/ClbJSRMDpWNig==","shasum":"ab48c77c522213e31480997104e9a95d114365f7","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.16.1--canary.257.f9cea8ce416d0871507b4c97eb971781a2531a5f.0.tgz","fileCount":107,"unpackedSize":2075423,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh8lb7CRA9TVsSAnZWagAAHJEP/RjSBY7j2CA7U51eMvbw\nqE1y2ebRLI4uPss5bPnG7p9UCcarwT1HpQLVYNxdvK81AxOY7Ofpzcddj6DP\nB8FKFzqQKEaWXSAl6E2Sg6A07EYGKsPPTTByZ1A0I8VjB995fXQ0BpWCKw6D\nwCTLE87KbGwnsh1GZcW3evJCMav/mOiAmwo6M9LwNVSw5kfTqpIccsyn25Cd\nD0zx59xbDpnc4RzKJiIlPmVVsl+781iP1FOkeKnbR2eWX9nmVU+XSZPxt2l9\n74g1RSUlFwgen1eU+mGJutf3BWLfHGUehJbv2KAbbOMhr/Ml3rgTKB81fxud\nDbpsw7emg56MGxq2nU9lOA3e4jkWh04iczSwPBp4PVYZVDVckja5CzsDSk3R\n3D+BiYim+iekQqrTwjCQ3jiFDgw6urSnlKTKHAGo2BTKdum034doOc22pnfb\nwTjDZo0vdsC8wjb3ij6+PTgNkIJTL3dBQoLDjpfl+8vW0tXGJt4qjTA6I/x0\nE0rhhz0MtNm42fekfw832VKXDTgOFu5qxwmuPfpPC3KBQmj8ELXt1lpazgqt\nDWwPxKEpwvAOZGwv4+5gUCXi6ZrkpPzcW9gB3tBdAVgiaIhaNPR50OILjyhp\nX0q/iKLtxZBx8YIX77zizulxmX3c9CFTQGp62FspRVskrqDgjP9pHVpxvLH5\n8dVp\r\n=5/mq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEEMgqNisxLNzk+vmstQAe+Vdj4oIpp0v2CvVl9LVDffAiB/ImFxMl+9gz0rWYO+U4+QnkttANQ2SXy8Nv3x9RJ6mQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.16.1--canary.257.f9cea8ce416d0871507b4c97eb971781a2531a5f.0_1643271930996_0.5737817560638536"},"_hasShrinkwrap":false},"4.17.0--canary.254.e45e4c34ae67bf4d0b3e4add1a826a1f83cc908f.0":{"name":"@sberdevices/assistant-client","version":"4.17.0--canary.254.e45e4c34ae67bf4d0b3e4add1a826a1f83cc908f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e45e4c34ae67bf4d0b3e4add1a826a1f83cc908f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, наименование поверхности. Возможные значения - `SBERBOX`, `SBERPORTAL`, `SBOL` (СберБанк Онлайн), `COMPANION` (Сбер Салют))  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.0--canary.254.e45e4c34ae67bf4d0b3e4add1a826a1f83cc908f.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-qml3hFzYO+7BFtAL8ocvbrjyD34mcxw17oph/GlvHiQ1MpAdGRlESJ+EVj0OG8S1KIlj8YKsni+ptniVLK20lA==","shasum":"61def678545294146431fe4efc99718d615c65fb","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.0--canary.254.e45e4c34ae67bf4d0b3e4add1a826a1f83cc908f.0.tgz","fileCount":107,"unpackedSize":2076803,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh8+I2CRA9TVsSAnZWagAAVxQP/icHkzmmz5Dwomu+apZb\nzwQr7YVYUMVSDspFNcwD7bORGxYNtx2A9yG3a0qiZgtWe77VfEce0RWsg/76\nYGfaFASQOVoVJ9gcLAMenUDJmYS9eP4xo36dD03nHGuCv+QtKostr7KJ8H/R\n/HWjY6JfqogEdeNTMofomhqBuLrHkPowwanponzUzWeThqKA+AUjMDLM/TBC\nhnmCWWxRkiTTxWPJJnDuUnnUrF/bOQ+GCa+DvgmZ+dt2YrvIchA7Jd+dlN78\nIzJ/RpvnNNEWInmTPjDq5CSSqFw22TxK9xhhwQxyC0TykrsJ/wKhSpilkfaq\noYReldwlsQKR7uOCOyCVGbOizeNpBDbwWuR4QOFGK84MAyzgzEuJlJ4FQt4i\nfcw4sgv+KH5Ymd33rds6w315rFgv3UU6WFSSEwxKbQTuPcUUQDB/k4dmyvwi\nF5BXpQzkSER2UNkFsLFF6eMqL9hdiN9w9R194Pl/C/Wa4nyMuXNFr4BqdumQ\n3lIg6Y9r0rZ1pVQAEvazNvg20zekO6PeZfeGkG83RT0vXgCA4EWaeanIrErz\nQCrQUDPNLS9RRTkEP1HRiqItJ3/4mdmeEbFARRrrfNKoI/JfLkICSFGwzEbw\nP38ghaP4d64yXqLQcJ6TyW4jBJkDnW/KzhdnF9A6ghSLO1p77NkgwvZ+27Vw\nloSd\r\n=XfQA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCz24cVIObixwkT/M/BtJwJ9t56jiiExqwltWpcuAINpgIgLmlvchYKQcEX8ZY0v+Vcop42r3Np3IFk3+gW6DyGWx4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.0--canary.254.e45e4c34ae67bf4d0b3e4add1a826a1f83cc908f.0_1643373110092_0.125523079616912"},"_hasShrinkwrap":false},"4.17.0--canary.254.1255e8d9f29f84606916c721f8af87147249f87c.0":{"name":"@sberdevices/assistant-client","version":"4.17.0--canary.254.1255e8d9f29f84606916c721f8af87147249f87c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1255e8d9f29f84606916c721f8af87147249f87c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, наименование поверхности. Возможные значения - `SBERBOX`, `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (СберБанк Онлайн), `COMPANION` (Сбер Салют)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.0--canary.254.1255e8d9f29f84606916c721f8af87147249f87c.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-UDfCpuLcWwQe7YNvf9XpYfpjM9qYJl4HOu296TXkEq35pLu7+tvinqxctck5OAiZzW+ZuGdt4LImpb/hCcbaWQ==","shasum":"49f4cd0e745ed3f17ffb8d47c80eeaa4b3c83663","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.0--canary.254.1255e8d9f29f84606916c721f8af87147249f87c.0.tgz","fileCount":107,"unpackedSize":2076863,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh8+ZMCRA9TVsSAnZWagAAtwoP/iti4xU9+8Z4JnefOAlD\nd6h7t+wKUBdl3x8SLxgWge924tO19iMKvrnlxYGrYnPn0xx2qtHPTt4miUx0\nyf64FEQcn7DxzUvlnUx4GnP4DkTpTvZCMDdON8Ce0D37Vp0eVEUUjpSQrZeT\nbpnP10n2Rw7PQ5amfATujdekuP7wSZwdWHouDb8dZm96ctsvueyJyDvstHng\nmmQ2xu3Yon1vzjEFnHU71wm6khRiD9pEsqPfTR0mdUcifOEWJQVKtGGQbmPL\n1KmDgkZEuiLScRF1ejKpH019aUJT3Sly7DKhdjU3MVE6LVP7LMluANq6/zPJ\nlj6L5iSQSXm3/EQYiYzpq/QaQVKZ0NQedmbNIKqfNOCCAZ3KBaROhBcr3v5i\nu0dMOfBUiuucUmpvPbVD+8/db6TTUcHGVNOMzu2VFLRI/JXJLZw80qKIbsVJ\npZaJyVc7mt+1Dl4Akxksd7qY49txi4uPbSVYs476+JnMKJWY4eX+WlJjoBHw\nsiPzBYrCDCYnD0OGmsah+efrr89gqlXgnRVTjsf5uFaNttQEOm7HwFx45s1s\nP7UeOekAKFcdwXFFjIt5fq2RbT7XHP1E6eUaWd3twz3ErWKxMEDQFez5vdRp\nOpFwLQ7qck8uybRg+kcOPipKG/BcpZmi1+8zUvHS1VybMZLhXa7zeVmS21wg\nZwNU\r\n=J1pR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHK1Zuahv33Eh9Ju43f123A2xFtf5bh1H9IN0upDfXwSAiEAkuebznsVIooAdUxMQIfq+gbvC48G2Y+hLMrFy6P4OBA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.0--canary.254.1255e8d9f29f84606916c721f8af87147249f87c.0_1643374156284_0.017806316690593293"},"_hasShrinkwrap":false},"4.17.0--canary.254.a9ab08a9c7236f0aa8e7f38e5ad4f78d3714afd4.0":{"name":"@sberdevices/assistant-client","version":"4.17.0--canary.254.a9ab08a9c7236f0aa8e7f38e5ad4f78d3714afd4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a9ab08a9c7236f0aa8e7f38e5ad4f78d3714afd4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.0--canary.254.a9ab08a9c7236f0aa8e7f38e5ad4f78d3714afd4.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-nJlFc0LD+sxyROsf0BiZc5F4egRxbdCIERoMNz9JnXHy6RhhKj5XqPHj9l9fE4DUITezGjNBdYN9kGrId6l+mA==","shasum":"a7c327e77c015ee2a070db6e0fe8192ddfc613e0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.0--canary.254.a9ab08a9c7236f0aa8e7f38e5ad4f78d3714afd4.0.tgz","fileCount":107,"unpackedSize":2077033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh8+oSCRA9TVsSAnZWagAAigYP/Rhl1FPMbxsbbrC67Xkc\neZ7Co1vpPCJqdwxwPbqhAF66KpWtCQOJwoD7G0xwbMMFp1hjBvar5DxWBJZB\nO/gvVD5sQqGpQVF43j5Sovj7u+TtxMHaSdwoNfYPI8/3cqiPtzaH+BaoMnfb\neeEDSUJTjJpQAL90spFT6cFZWfFJH3zFA16iGqYu6+hAqb7fPsuQlGKaAC3G\njNdBHnfAgMM8sklJvYajMDyCrKPqZ1IWeGXKxIJRF6O9NtqAIFkWq8JyjB9I\n2wK6ldRItR121Uu6RJoB/lmd9GMVPiMNs14Q+DZSC/D/4d9PNeU8xFVXXmJz\nMnRwgC74PGb0rt+rHKq7sRdgaN09UejGOIm1G6W9qg9Xhq0MUXsZwUlaLf9j\n9WpCVTDmYqv8WMiCp5zUmlu6kR/i8FHptr8V5o7uU6THrvMqVyuDpSgajNND\nD0sZtzJFzT0XBpwDBhWNK2x1rARv3zpzjk9qAf4EfeJuDgM1lzwUfn2aZu8O\n4Tf2MI1l8hFlJh/0t2ijmhb+0diUu2sT2kWV0RmhA85EC1mTd/85OSv32c7o\njb6Nac8jwcZltYkc40X7KApbi39JA0d1eLaUoGWfg1jXh91z6otnbsGzlRvH\nAFyapltHL2F4L07Pm3zm2YiiECN0RiBI1ss3/ouX2UGQTFTYOy/SD6Z9Z/Wj\n1sGi\r\n=ZBEV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH8fs3i1z20BopFGYq9ck7gbD8z47INXTdKcpg89B4FNAiEA7CbGGqDfkzik7FleMB3tv0g899pEhBHgh47leYHvo4Q="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.0--canary.254.a9ab08a9c7236f0aa8e7f38e5ad4f78d3714afd4.0_1643375122392_0.06730560389863682"},"_hasShrinkwrap":false},"4.17.0":{"name":"@sberdevices/assistant-client","version":"4.17.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1856234a14c27f133702acee91d158aa9b846fbb","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-ywifIcz1qt/YabHd76H0dfosEJ3sl22nnli71zrgBuzduIoWuvFjiGrMyr/WBkt74tu854KcVWFAco3hdokUMg==","shasum":"b8992238f9fcc850b6542599372d46f2312e023d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.0.tgz","fileCount":107,"unpackedSize":2076928,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh8+rPCRA9TVsSAnZWagAAWOYQAI1FXwfuKUnXLw6s6aHP\nPmSDf3FZtJ0I7ptha6+pgqjkMfQxCIgrwIJe8071wEL0vfcFSw/fZ/3fGeLw\nnL1RTezxTztF50Jwz0zvZ+LTt4dHq53r7xj2XerxmtVrqeDZnCbjtkGfsLE/\nwkoOh/9VzBZY0gFb3XfcFbX7qUi5vq62fKQU9NwBdHhyTGB8b0uG1P3bcLlB\n6eDgPWie5wfg7QCz0HTeDXpzR25GKIprD0Rx3IwBGkwE3tmFtp8rCMPZ+zCp\nUPaU6UuC/0Bs0Zwa9xIW5QBWcIX5dvOwnGQkkQjumv1v9JxAvVg4B56fMkZn\nMw0a/U5QAjNq+FOqp56MEHupj1ZJsCrq+egl1ya0GyYT0lcPRWUoFn2zEx2C\nH0IUf2Sj+jWdTBPhjc1ViHbLvnowG93r7NqxW1zl832dh5ASVUnR59kwc1MU\n20dOmJM/JIa13z9FhK0KmkxvaX7Vm05abJ6tj9YOLGxSpOctUyeJ7la0S1Ss\nZ6Uv+RTbHv0BODf5/EOSAZON6QQ9Zx8HT5Gw36SOKiJWqIHgBP6skllBFatJ\nfEFPVV9nGyWDXgXd38scwGcKCzhGRoHDYegC7ajotTcs0v0WnQ1m9CilL4D8\nQfx4UtDvo3Nb0mofy5DE8QcKUKCzW6HnNI74IAwLcKJlVPrULLU5sZU4wQ94\nq0PK\r\n=aleW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAvnv1bI2Zumu0BexunSUV8n9ZzVK0/o3xJeb9gyaIVVAiBuZ6t99tlVzeJC3oVRc8eT/3CPfc4GG5OJt2DLdAqwDQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.0_1643375311404_0.4020690750057341"},"_hasShrinkwrap":false},"4.17.1--canary.258.3ef2f5051025f76acc3a499189c16cfef5f29631.0":{"name":"@sberdevices/assistant-client","version":"4.17.1--canary.258.3ef2f5051025f76acc3a499189c16cfef5f29631.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"3ef2f5051025f76acc3a499189c16cfef5f29631","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.1--canary.258.3ef2f5051025f76acc3a499189c16cfef5f29631.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-70+3qdsgC1yUI/LPuT6CtWPs2BhvuXzA0qlsgE+SCFV20Y9GJmorKNqkCO31EMaN52UL+WZn7X7z3bWHAp2v2A==","shasum":"37ca11fd642cddd74b99cfed70d86c1cc2868605","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.1--canary.258.3ef2f5051025f76acc3a499189c16cfef5f29631.0.tgz","fileCount":109,"unpackedSize":2081677,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh+PEPCRA9TVsSAnZWagAAUgwP+wVsFM5eip79cYSA4dsN\n+nrR5fDZfMy6BiF/enCyerghatvrxje0RfPnSBj8p1ZMPBtqLiGe7SDqXKIT\nQkP/38aO9ug5Lt9DqMcaeQAnKveF472/B+V6ckR5hn0Njn72nOeu15l/3WsL\nDkZ4hRW6Yl0mLrrly9cT4X5IRX4wy6XrYYS6R9eB33gevEEh2UXP9tJ8a4E8\nqG/LdjUPXe6gQ8mYCVqLgn49CPhqqkSPMMNBzc82Q7eZ/HqWDJWtfJPbY66P\n4dbGrMP6eilmsb3veo29Gu/tnBSm4fT1E0xNEaz4ukl8Q6+6LzprR5zz1yMi\nj15GVw58ocjR8SL+kNnmVMjXezamGPOGwi3WXJiz2QZO5WmsLgvcPDm4qlOj\nJxylTcuk/qNHwaipPKUe6bMDddsJeBvVpme8Y4X8mBSzVNHc4IA9ZwGPyKNd\nPtUregxJgV1ioebG+guQfGQbjYuaSC4lSXK2FNsnovrQAUMs5gPOlgK+wVlf\nDJ6sezlTY4NHWke3Jqptt7bcc/Xn8MXckbqYriNcHP8bdZP0UQSjkoXq4d5X\njJLVHrqgBBA3Qkosl8BJHDPAcwE2/ozO2aSJuXHwOGksU9wMlwuq5seowwZS\n/kVe6ptYzhOrsNcPZ7zdu4lzX3mvlQ1jbJ95rdd4KkoKJCGVAm1/VJHUjld3\n0jF0\r\n=FrNZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCV0gApEwrLJqp1tgdbHrpm9NlwSdaSx7uo+4PDy3F4KQIgU19X1Z3sDY7PnXabKWF6QctO5nLlW94vQhhKR3p2oV0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.1--canary.258.3ef2f5051025f76acc3a499189c16cfef5f29631.0_1643704590838_0.9400908541483086"},"_hasShrinkwrap":false},"4.17.1":{"name":"@sberdevices/assistant-client","version":"4.17.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"75b11d6d713c0fd71d03f36a86198f0a4d189134","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.1","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-7AvuuMVRYOmf5UwczXf4opyrtif71xuYvXtLmzT5s5K6FWbOsaK2jbn/C31ZIVhjnAbi2S4SnJVZweG6n88C2A==","shasum":"075615d596cdb90284fba629973c5983d75e1ec0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.1.tgz","fileCount":109,"unpackedSize":2081558,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh+SnpCRA9TVsSAnZWagAA9SEP/2NApHjMIe1nfE0wXKlj\naVNS4avcJjHlu5UgU9UdZRAYccj9CqvKC0Ekb3nfAwdvegJG98UMBSSv9wgG\nWm8fu1gUppbqI0E3DXe3GqQoQh+76X5ot/stOSoOkAV0DsNRX8TEG3n3jg4B\nvn4vobg7akFVLxa7/LkSAYNd6cSmfPd6PjfImhDfzMN0UitNBPM+fNh10B5e\nK92RJINe0NkW0xSWx0jL+vAJSnZZZDTIJryjA+/S010T/Hs3vWJe5aG3RlM5\n2bF0as4OKfsEfJPFPzFsIxGoOgXm5rWdIsVO9Kug5Qvc3GwDKKL4dm/tjKm8\nArVaL/1LfCNxN+or6ZyAg9QJ392ODtpXH+8CMiNQxvkCkndZnsn1nocpE9Bd\ntBIte7mMUFCCd+URQ/1ATqJBz3ERzmTXTaWMlIjuwWLr45tqlRn2oaw9oJCz\nDXeYV+PVZqQJZtgO5IdzVWNlEsJz7YsrFzVk9eWnzE98qOWnnP4uJHa3saE4\n4+FD9FvYzqwnFNDCt0FwSi5rrIMnP8v/oABkraO620GjfPdZnQPc/8wS39sC\n5ssioN1I43vsBcB3H9jpUk/6hPkvXKmxgF5OxB2hT8yMs0jtSGmm49yeEtFb\nsSCtvfvwqN4zv52NLpe9Ct2wf2w9cuYy66bvk9duCc62x0+F9mZK98MGUAMH\nxf7N\r\n=Mkmr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDxTazbXRh9dJlI7QzeUvNf2ksgMluyjcgK5+TWNuPZPAiEAjQe/vsuzbI/YWfDonyURiMlxokR05VtvcorZFDnt2p0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.1_1643719145148_0.047044381767721966"},"_hasShrinkwrap":false},"4.17.2--canary.259.0ac7aca64257c5abc68213a662b050666c0eb68e.0":{"name":"@sberdevices/assistant-client","version":"4.17.2--canary.259.0ac7aca64257c5abc68213a662b050666c0eb68e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0ac7aca64257c5abc68213a662b050666c0eb68e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.2--canary.259.0ac7aca64257c5abc68213a662b050666c0eb68e.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-9DRX8RXIr/ofIXEN+a3kLp6l4P8P8Y8A6t8WID2N/CtQuk8w0tNSRX9xaglvByQWtIkYgnv+OAGjRlSkgzE3Lg==","shasum":"73c10d7e5ec68406a4d4f13e4f4d7f01bd2a3dbe","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.2--canary.259.0ac7aca64257c5abc68213a662b050666c0eb68e.0.tgz","fileCount":109,"unpackedSize":2081998,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh+kcCCRA9TVsSAnZWagAAmLYP/2YdVhyLsiMOQilkFUg3\nt+W1QPR0PmbORFigEHqzJq6cv6LrHtBJRqdQva0QgeN2+Lvl6LonuVwVRN2b\nj0deTTMfsY+NZjgaWnO+3DBvlFhEs4goNZp1084zZjbnhpjDOkH0feO6GW05\nMhTjHds9iVAkVBXKLhkrNVC3xnDAsfVRuX07N+FbtW3VsOUNS9p9znSUm9/K\nemyyrp6NUZCWyp6RCN3GaRX7NnO/LSukpD7vBrXPQCCfPK07k2UtRJCx++nP\nq06SqosjddfQnHtGuO2vdUEZHeRfD4aF+gLSGjldV472iRlvztjadFliBm1X\nF5WsFDVIjl8sIHOZq/khv2UWnpIVR9szM5mk3ubUxnVa7w7eDsqj1agGvNkG\n5AFmvCp0KqO2OYw3YPl+PpKcWaKwXMHhF5bLL7NiKRi7oobVyL9Xc8qpr0q3\nx+P2m5VlAUIIEtrM2RG9rdKYUgB6MBxb4QJ/bMXLWglM5e7t5Vyq4cCVMCnj\naZht++SLE6snf/UxIfVvlE9lFeC7I+V6wd/m5kbFRmiaDu4Y6KcQO11LrvcZ\nKExGM+nggVxQY8qgnYOhWLZWfrwF0lHoXPbs0qboXA8vD0ULNHir6FkHS255\nwmX3xAFlZftTz/8XpPz0Z8USOsSwhBmKAG1suuhUiiYp4GBI0QpoENRKs70+\nGDsI\r\n=7j6/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDR7IppTHh5x8+4j/6iMXp1tpFCvBf1je+fuI597SIVrwIgLCjURgQ8DRr21k4bHEfzyy249Fb0BtwSHv8ClwhE+Nw="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.2--canary.259.0ac7aca64257c5abc68213a662b050666c0eb68e.0_1643792130111_0.45373591306236216"},"_hasShrinkwrap":false},"4.18.0--canary.260.5c34b40d4589f9f06e874b4faf485b95f47fef9c.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.260.5c34b40d4589f9f06e874b4faf485b95f47fef9c.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"5c34b40d4589f9f06e874b4faf485b95f47fef9c","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.260.5c34b40d4589f9f06e874b4faf485b95f47fef9c.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-kYW34kQo6IFly+I2O0IYDm4MVHPkZkjEAToVjOwcysIFoo/zJN0qIf3A8f8PqCjHLwbC4ZHVEYeBoyZ5/2+6sg==","shasum":"f7f47850d22963fdbf10e400ef73704c34b96bab","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.260.5c34b40d4589f9f06e874b4faf485b95f47fef9c.0.tgz","fileCount":109,"unpackedSize":2083331,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh/B4hCRA9TVsSAnZWagAAY5cP/ib7eOny7k6yfy7DKGAG\nJneUH7h/TbwBKL0wFFLbXXcuOZPdMtI2oIS3GUbvj4NfmxBZ+wIXOQsogGen\nd0+H7R+1RrlxTmHsOTYZJKtAJZXH/dZoHy+fmHBmprzz5eGqrHYIce7x5LPb\n67YDV7p9lkHNXJAIuy97UIJg8X47G8iWIqNz8Ilfxd5t+CY1V79YPAMNNUIw\nK5VHgPqDelp7/y2sxFl/6Mc970RjtiGwqg+HXlTD9/vl32qlEfI826YlCrwA\ni2l0t2IIweNqV8G6QVlVyrYlXZAFLIsbUG+AYFAu7gsZVcDBuYAZ1fG1dok6\nkiD6d1g/q+ZWS5D5Da9K40eMlSzIpcjyOmnHsjvlnqDgTIRjCVa9d8ykn8A5\nZsBJ9EO7XQSSUdtQSa4lZ+O/vAADT0SkcpzxW8TfiOo4PBzNLTHi8dGgTuf0\naQpfxHfRyV2v45OJLSJZIuSVgSNPkd0DJfeCyYaAadx4MrB4xs2m1f6jXcpZ\noUEhCjBEAWkV0ILvGUTPSrSDbpeXyK8B783CjRGkV+mpRjlnhLlpWDfocCqR\nAQrG2+hKzxvNRUUWJUfIJXpdjsVKzibl2XFltqtfdkJJ6wIIeueBIWbLNpbw\nS9KnLmR657CWHw5IGm/EnQdOGzFt3kc+3il0NDO2X0h7jPmcVFoTrw1Z0yPk\n1GGD\r\n=53/N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCE1C7EVZHM2shvE5GUh8KnOEB21cSDyXWLQiO68ZJKnwIgJ/JMZmvTA2i9QCWrLqo3d0V7LQE7E/sLVzGATYRMPg0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.260.5c34b40d4589f9f06e874b4faf485b95f47fef9c.0_1643912737100_0.740192895382779"},"_hasShrinkwrap":false},"4.17.2--canary.262.d6f9696e85d65b851d7329c59b84e402b0c223fd.0":{"name":"@sberdevices/assistant-client","version":"4.17.2--canary.262.d6f9696e85d65b851d7329c59b84e402b0c223fd.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d6f9696e85d65b851d7329c59b84e402b0c223fd","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.2--canary.262.d6f9696e85d65b851d7329c59b84e402b0c223fd.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-x58r0fA4SAQExQNAhsWOoggV/5poHO46Qi0uooIT62sdV/hfQCr3tYJA7XQF2ADkseCQ8w+29lzPV9XuA1r4lA==","shasum":"6dd4d7df66d2829510a0bf48d845a13fe0955851","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.2--canary.262.d6f9696e85d65b851d7329c59b84e402b0c223fd.0.tgz","fileCount":109,"unpackedSize":2082152,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh/RriCRA9TVsSAnZWagAAtGIP/3gos4K6x2+IwsaKRzIo\nweyzQ+sRP69b6uf+kSsX4Kg+7RXJotcIHOzI29b8U5CPnkV0xHbG08QxLC4V\newEQIC1oMYbgvZVUguc9r5i+RLy2ycajJsXRCG9LELESN1x5eymgjSTX9Q2w\nr6nln4DL+fo20c9lz0gMc5VTvS4LJRPxzxPKDV0t+swaK/2eBO2Hto0FkQYm\nqtxyAcpwQkoOn7PDt6dHU54ad0ajaRnIpXc7NKVDV+t/oewCrawY9wNPx3xv\nTBkY4U2gHhJlduyGFYEw3T57r6xBFjei3FtE+slk8XnQtWvKdW7HqVZu2Rgj\nqxl0bApkHW/QShyaYfoQgnEC2SlvTF5fyqwbJDSpsW7wFtJY95cNubZSZr0W\nCto3L4F4niPQJ8tyAPKf8tVe+KEWuaZFyFs34rJXm6pWypqVx7pEXAz712DB\njKHJh1WbAeh98EFE1VSL3/fpA9mXhnDUWNdkW4Mn3KdkMmlK0X5HVEIKsC9t\nCV3eQZKV1Lbjb9ND++1Uh5IgvL5+BWmzwFw271iI6rF2y/j2e4I7R9gZ/r6D\n60OrySxS7lLrUXWeLUHepqY4PYdqed8UYmoHpdfv9kmUhi3DbFPlVna2xDo/\nveaSQmE+vhD0VprGwWWs1U6tUlH4pL9PA13/M/WiUVewLDl/F8iPpnccPMia\n89Hl\r\n=JByK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFWMY03bpFG/AP4mHazrCxvVMNBnSedmTchstQORoAtAIhAMlWZiHNjU/hREDq8fr63xhZfpSkDJlrrkMB9uPgFwk0"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.2--canary.262.d6f9696e85d65b851d7329c59b84e402b0c223fd.0_1643977442059_0.22638162343931523"},"_hasShrinkwrap":false},"4.17.2--canary.263.4109985eeb2ba9476ba3167c245dd14558586a5b.0":{"name":"@sberdevices/assistant-client","version":"4.17.2--canary.263.4109985eeb2ba9476ba3167c245dd14558586a5b.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4109985eeb2ba9476ba3167c245dd14558586a5b","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.2--canary.263.4109985eeb2ba9476ba3167c245dd14558586a5b.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-HOabZT++LXhIFabog6d0HecFCSAZkG3xIZk/p/yk9zwOaMemXQl+tw0EDQ/2C2o1NNkkoNQ695SwCwh3Hud7mg==","shasum":"baa0389630f70ad892daece27cfb40d43200ecd0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.2--canary.263.4109985eeb2ba9476ba3167c245dd14558586a5b.0.tgz","fileCount":109,"unpackedSize":2082170,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiAQ2eCRA9TVsSAnZWagAAid0P/0RfacoBZv5nybV2wTPs\nv+SOjLulTXbwiQCucyNbXelYOBWIT6UTqBRgjxlgGfAd1heV81KMfAc2E5qf\nyG3k41tM1dpmeYTzR0r60NRTy/nhMDfjpfpEXp57Ijs41TcXaJfwysV++4oN\nwGNUZb6c/M2J6jQBw5C3LyZAM9AywegYsjmHIG0OCyYrI0dfkehM/oF+ojL9\nqpOnU9bnU/kmUUvtAi7ZzkSEvFDOYnRSeMaJE/IaLOHdig674nqvjMS9QqeF\naMlgR9SwRicdjip7DYw7X7x7/UjAiMheOpT/oSlEc2CnDNXVu3tHzUqYWsdL\ntgwkZQC/kxjXX0+VolAeVOKCndyaPEQjaMDXIWBLolNR/QJcGzo5JsM9fbnN\n38fmOqul7quPTf/k5Ggoi+3lFeVz5ySSOfBRcxGJJZpKd9K95Yn9K/FZP/h/\nX/UOyXPyarcEAGyxRw/u8QMuGTmkXPcjoyOGEaj4Jgxt5YEdOHpS2rrkX/Lr\n9XfJ0Z1yrRJRBwujajwy9ib/YWz1hPY5eCmB1cwS97+yXAfWVP5E0ztmmj+9\nasYJw3Xm5wLMU6Oqt9JBAuidZ6UW6Ooudfa5u6GxK0ZUr+HZQrnd/m0k9JwA\nH7s/pfm32UtDF/CA8GEnG56lFuXbFu5j8sWU+zPJM1x+UyMNt1X57KQmmB1O\nbglK\r\n=iJu0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVMmpGL7NPGyXlNm0GzInvqH5VRl4X1JBN+gMfBfxCgAIgOtb2qxgkW7RusLfIOjh6u9BEKcYjqBzvqwpX38taPyA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.2--canary.263.4109985eeb2ba9476ba3167c245dd14558586a5b.0_1644236190490_0.6030884911796714"},"_hasShrinkwrap":false},"4.17.2--canary.263.c5e13fa2a6240c51c1c764a54b120b4b6f3a16d7.0":{"name":"@sberdevices/assistant-client","version":"4.17.2--canary.263.c5e13fa2a6240c51c1c764a54b120b4b6f3a16d7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c5e13fa2a6240c51c1c764a54b120b4b6f3a16d7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.2--canary.263.c5e13fa2a6240c51c1c764a54b120b4b6f3a16d7.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-fEOunpub7GLP8eXUK1X9xr6gqikEnUETaTD7dheyLiXMUrosy3Vi+Vjs5vapoRdsuxltKijurWstYBMPybj/Og==","shasum":"71baf2f5004acfb252cb67993a30a8dbab532720","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.2--canary.263.c5e13fa2a6240c51c1c764a54b120b4b6f3a16d7.0.tgz","fileCount":109,"unpackedSize":2082366,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiAUY1CRA9TVsSAnZWagAAC9oP/0UpG1GB//d5eeGLclig\nDm16DY7QxoRxQM823xits/xC/pSCgIFzlHKFT7R2cO3BpEyeoNn1e5dEIxHy\nPbbjoz0xDN8aJZYMReoIgYJAxsdgBgdztmrxoHeoo8ZjoGjGW2+tAoFXEQ8a\nwYJuBMFsW9YqyVOCTWHG77ARW4KmrOjcWZk+Tftt2WRSR0DEy4comxT2kN2L\nEHtkQNzFAysTWg98GYN8iq6/iPQ22J1UnA1VeYNw6B0AgtzdWLXV/yeosQnA\nRqTqw5sm1lzknjoqUhLLvVky6vhUINR0pOsCxC1pRjuavp/c5H6uMIRmI0I6\nXyLHmU9cV6ox0Xeldyu6LKWFzK2cpt1UEZbE09uaoBV34AxYk5GF/qammyym\nahsDEnbtFhuQZECh+sFLA//wbyeDns2cIIP9IttcbbM6KEgdJR/x5tNGRBot\nTvYeCKbXDAj9dAONq789GShuWqoX24/EBCKESVJobyEcd5njqoHkFEAp4gg7\nOk27a4qA6jLMbWj6AHTcodg/fkAQJEruVp9XqCN8PYfGJTszv1zYxWvoclpN\n1B8RwqzJvGi1Afc9g0b2WJ8uay67Vo8WEGb/tjNUPkbpTwCyC3QA2K9lQggK\n6eEzIt7dBxqn0tfQflHewc32H40U6bvB5RgzDYqJWkX9WLdlbg4Z5kbbIxQe\nPI2P\r\n=KzEJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmAHoO7AcIN7TQoqTy6b+PRaJH6ceRhUqkUfC/rwPNuAIhAICoc4g6GOhygkP0ynBr8xcVcNSgeKrAwRvhYSJJC9U4"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.2--canary.263.c5e13fa2a6240c51c1c764a54b120b4b6f3a16d7.0_1644250677054_0.43877903651750505"},"_hasShrinkwrap":false},"4.17.2--canary.265.b706e33b583084fd7abeb434dd8d1c5595c2224a.0":{"name":"@sberdevices/assistant-client","version":"4.17.2--canary.265.b706e33b583084fd7abeb434dd8d1c5595c2224a.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b706e33b583084fd7abeb434dd8d1c5595c2224a","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.2--canary.265.b706e33b583084fd7abeb434dd8d1c5595c2224a.0","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-SKRd6KtYJpRz6o7YdoNlbnuR+nwR1HFZxCYR2QVLJGY8aKyiWq7qyQafISj+6boCXFXJhnmOqmb+SNJC/q51KQ==","shasum":"d3994fb6b8c18443b9fc0d89f6b8c957f676d59e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.2--canary.265.b706e33b583084fd7abeb434dd8d1c5595c2224a.0.tgz","fileCount":109,"unpackedSize":2081912,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiAk/eCRA9TVsSAnZWagAAmKUQAJHQ3jDN65CEtPE8FBlh\n0ORkwVhOxhhS0Y7nUJ+MJR8xw00UuKIszvAYdU+bH8SElzTqPMmYVpZSf3i+\n0bbHz36QB6GSGIaYF4KX4x8hVF3vfi/dsqXlkP1aWPEjeWob7+a3aDegaiCt\nACqpoJch0ywW+pt+XGjXOAfW8JpKxfDLygQjmheJCllcWPVOElnay20BB2hZ\nqDG5JwYUqGLejpip9UOrN/CTi2CefP2grdoxO2V1U/pYhtKumRMunfgWdGg3\ntatwKKuFwoxTJ6j4rSzCHlx9LCzMzbs93TJgXMoyGzVGXdKn0jkiH71lJeAI\n3Idz7V//GzjH5Jyo6R9zJpSQyd15mJh7G853TyBBtFDjUxz5Jvfv4TaPINzk\ndEuObA6Hi+0kNE+oiRoN4XL/Hl82aiOuCfNn0NUtqGjnIlsZT7DkECHxvqau\nZ8iZc0pwxMCtdukysYeT4ASmocc+LSkONyXzVkSfgdi1PtoGpjfI/5b/jOfb\n1nwdOsneQRcC1H6qmaw9c5dOPQj7uUOAJSoaQrxbBZaQHaiAlsmSMn876Wk8\ngLSMzMm3CxeQgrPZhHVZ5DthuaPtyFbVozlPj7sqdD87sV9F9QsOF4bjTEIC\npGtTT8WdICQYIdMSEEtRK8GE0CgLsTv4+X+5B3oFNZ2GxIkrRthwZ3lwyjvZ\n4OAz\r\n=GmvD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHhCUusQ+biu4smqdpyYAURzXRZdYbr/l+7+Vk8GDgO2AiBb7NZ2GtfSeTJvXTpG8WUEhe+n/Itz0GFM7EBvkvYogw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.2--canary.265.b706e33b583084fd7abeb434dd8d1c5595c2224a.0_1644318685956_0.3553482559942365"},"_hasShrinkwrap":false},"4.17.2":{"name":"@sberdevices/assistant-client","version":"4.17.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"9ec742587f9f9167c6d7e8bed1280d5c8544a751","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.2","_nodeVersion":"12.22.9","_npmVersion":"6.14.15","dist":{"integrity":"sha512-PeVCOKzwGm+hj1Z/AmfKhAqiQ/+voWlGzeewpt1rrElDJvFIGSMUfNChfyxf6mg5jMSpeKiCcuFcLV+B6qvi8A==","shasum":"6f48ce03e325bcb5dba8bb0588b1fb500d2baaa5","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.2.tgz","fileCount":109,"unpackedSize":2082823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiAlYbCRA9TVsSAnZWagAApdgP+gNEl9iFsgjC1g3KPdCH\n7a4PCv5itgSTfuXod03tA5W+ij3oAxeF1trItrrHxOQHiDvv64/K9KBECX5S\n5Px3Q9Xd0aqSLrFWNvX6GUTI5zwUIvpc1w46CYpNyTVTG4q6CDItV7Xsf8Kf\nIERoDK12Iss5TJPE+RZqpyFb/V02EQrJHPT3CWO/q3WYk96MzWq6lTmYv3xw\ngt+72rMkjbIfkrtp6gK8gsluxSZFcHa3F83IF6L59MHegsNQbrStwoapX6we\nSc3ba1bu5THP5jVPG8CGNicQFAY+G8kl6sTyqx3XfzQKUeAMGuCNNRq+EBnk\nkdt5UwsX+cRPyTXl4EZMyXYbnIoTeWyrdJzSlOSgl7Ja6/nBxPwW1nfmRfeh\ni75g/IPmQ08rrXrHqVt5Uh//GSnPFW33Ekvn/atCBdSz422YVwoDZiWkLLzU\n8r/3wbsB2SUbrRum3kvV3vBanfvlT9EgcaB9NIVW2ug1cs1+EozTGbH+2Ngx\nbpJM4tBf/wyo8qydVZ/Bm+zuWyXQ2sAqIedT05Fnl6o520TGW/LLJXdj+NL9\nA3i+zoM2BWMDQyX31D5IL0gfQallcgz7IybGRlDLionFOIUkSQs+3x2TYp/c\n2HZmuXx3xJiXzNcTDqPlbbS2wzWPe9Zd9EqOcV5n+esodo/P4kmYgvRci5hu\nCOWt\r\n=hLPh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAvtzGkQtHCBl05Uj151VUiqDS7+SHOYH8FF3fzVcdMeAiEAvMn3Dfbc+fX4CGay7OjWKVUy3gSTCwA+DgwSFptQ6SY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.2_1644320282784_0.5240377435119743"},"_hasShrinkwrap":false},"4.17.3--canary.268.f78592c0dd23bae6234ff974ad8f1c44939a5801.0":{"name":"@sberdevices/assistant-client","version":"4.17.3--canary.268.f78592c0dd23bae6234ff974ad8f1c44939a5801.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f78592c0dd23bae6234ff974ad8f1c44939a5801","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.17.3--canary.268.f78592c0dd23bae6234ff974ad8f1c44939a5801.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-8OWmnvLUkNYuPdu9dwVt0u/ELb9j9p3g9zMoaDH5f7pibol0A37/oXPDazcTI5In5rVjDWK5+OPUT8Uj5P5MWw==","shasum":"e10369fcd959322f8f41e554ea76b50968f9606b","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.17.3--canary.268.f78592c0dd23bae6234ff974ad8f1c44939a5801.0.tgz","fileCount":111,"unpackedSize":2088341,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJiBlPJCRA9TVsSAnZWagAAGxYP/1NIA/o0gA8Hw22Zs1V5\nHB+9fb7Am2Sk0DFGmENFuEsWVqjJnjYzw4TJCLqIcLMjEKt5egtWAJxAizMT\ngaOROnJ7/+T8mhf9T/b8INe8HkS5nXBOcIEThvMr9zwOpYn9cX68Nf/9HBL2\nwJDCpwE3WT21cQ/vWMqraAa7CEJm5KaRFv4Ws8VyLPuGUTSVBlTUYZpxMguV\nYJDrwpF4NKN6hcUdLbBXfrzFcfDV3j7O2dX5mRqAyzHGxqWMicQ5gWvkCzUj\nIkbsrZE4V1YdmzakHCO2vRGd+leN6QbX4NHRZiYSDGJd4FjBQ5hRMgWjRHQx\n2F+ECj/rcY/izWMRYw4+KEtTjw9hdV/utA6TRSYihx+sWDZPibxFNiff5qw/\n8fzMfC1wQCokjU5KvsyF0H4NEbRWAlISZpwgfEbPVxvmY4Ae+RpmNgI5htgo\nl49pQmhvnpA2Ay27izgzWI2ez+gpCVgTbX+HxUCnRRBgpcsZBBlZcB85hShj\nRk4rKZrdNRoDhgdTuDp5RHoKZYj1SsV0uZIOkayz0HsFCICexcu0rWGDVLD0\n0Ohii2MBUrHDdeqrTlaqJenIOtBSGqW4fx+/AJZWw43K1tWiwwmWulj+KrLd\n5cHF16tK/mqK6iazoOlydJgRQxlm/IXf8hwC9z5Nd1hiwFpS3yy2zPPj/vOJ\nq89S\r\n=LUz+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDOQMqXOOP3VekR2iSc/IRGlOnuvnl8aC37Is0D2gwlCgIgBzupg0NmHjbLRC1Rnv7/TGRWYCNA42fOHw6Snm2W2Vo="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.17.3--canary.268.f78592c0dd23bae6234ff974ad8f1c44939a5801.0_1644581833061_0.29314533212815275"},"_hasShrinkwrap":false},"4.18.0--canary.269.2fa7a84e7a8721b69deab36944fe5e856ffeffeb.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.269.2fa7a84e7a8721b69deab36944fe5e856ffeffeb.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2fa7a84e7a8721b69deab36944fe5e856ffeffeb","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.269.2fa7a84e7a8721b69deab36944fe5e856ffeffeb.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-MqG0JPqVFjkun01kZhpUtRsqeY4liGYYjoorLNWbiLRHp0QmHT8UPG0o+yH9EG8wRszsGxAQy+R5E6AHYXzh3Q==","shasum":"9c7cd0d8f250ce272b24c0bb92a357878a59169d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.269.2fa7a84e7a8721b69deab36944fe5e856ffeffeb.0.tgz","fileCount":113,"unpackedSize":2085222,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiDmq5ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo+Cw//aEaz995E7X1gXRx2aWeIcgtyIOwS9S9jXcUoBn9UPNaIehpC\r\nxaHBVRCpHNiSEutZivT3J5p2EgDi9954SxSmGYF0sYvRrDdickhm8rbj7oBl\r\ny5nKbvLNDVh2XlcVdhilrFc9EbEjOXbgnqYPiRt5bEiWAspvDKd1O35qf3Ik\r\nXnCIApDUeQIYf5j0raO3FpeeuMBVn+5rqDzIdmDZT+PYkaCSozbls8F6wEMF\r\nXnFCmLvpRcFzyvN4PaBaMIOsAk0f8fZDVtICXWZy7r19mR8GGXDuo9x+zPJs\r\nEGHGf6Czfbr+JozIOr+VEyDEiHyuuhaR3shrRNIgzSvHlVviBYpRv9lQGmb4\r\nJWKQHWEE552sCc9NAUXaOXV4oQ+uTJjZgmCVlks52CBp0g9v9NnF1glr16wv\r\nQYA9MY22Hdc8wwh8D3OqdPhmsA/Uo0+wuK0VSYEmELW0suodgTPzu4PEvpPM\r\nskXQVLUotFxwzQ7eythv78GO24dM8iRhLIWGxJ9e1c6ZgovIaggdDhGxpZGF\r\nFM+hWEh31dNN5f3sZ5XhvEYEn1irKDSOGt/Rh72kDXRdj8ElMz66TjMOB0WP\r\nFLjoydo41rlL2yjT+fxKn0LVSfV1coxTpf1C7A68lz6bcNF+CBXYfhFkyE/I\r\n/W8QfKhFh3Lr+LBhxVUdDt30gP/mDYhPixc=\r\n=O+Lv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAtPDHCkYi59OsxAT13xmdowEMwgcSwfR0U3KTqB7/YyAiAn7vpILKQgBEXkPW3IR/kUtBBMajeFvxalhBQcy6xE4w=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.269.2fa7a84e7a8721b69deab36944fe5e856ffeffeb.0_1645111993418_0.410916141134706"},"_hasShrinkwrap":false},"4.18.0--canary.270.a4ae2b45558c80d1261020210a0f21aee4ebe293.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.270.a4ae2b45558c80d1261020210a0f21aee4ebe293.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a4ae2b45558c80d1261020210a0f21aee4ebe293","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.270.a4ae2b45558c80d1261020210a0f21aee4ebe293.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-eZNjZa9q6ec4pqBChNHSBjcJC1kn9CXAK6MgQewm8G9SJBpUyzQ5xphAsVx7w+klyvVf+7N76SrPwxbQ8mMuqg==","shasum":"505bd11f10020ca9370cfe31cacdee88aeaee2a4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.270.a4ae2b45558c80d1261020210a0f21aee4ebe293.0.tgz","fileCount":113,"unpackedSize":2085473,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiD40YACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpLxw//W3/fPmzbJs41ZxZ2PRsX+0UzBFz02MUVthK7ko/COMSSyipu\r\nQPrLO7VNDk93VIy42Z7HkuNkyfeDg8Tqz3jA1ywHgmmSuCiIpa5ww8JRlzpu\r\nGbblmJRn3pMfJvlZLrFuoxz3BbUnTIFFUSxxmraGe0F05D7VbW9evFOJ4fcH\r\nuHCdqU/vUIK0hC/MG0AxxhvBiQGvXB0BPCv1YPCck8u+Dq7hyfWRg7Ncgv0i\r\nUqNUjpHfrLyqOMxic3ycd6BB7AirvRGB8xj2ZoE36uaCFBvOoGLoO0IRRsOE\r\nQnJKNaYpFus7INwuSZomNnlzbll9jU2nZxOzcHQSWNju9olQh0dE6sUh3HwP\r\nByw5YbvDAHRPtgalY2LQvf+PCXCbj2KDGXX30bnWbVgfxFxDGUMTMv2lmcLh\r\nup2Cgckre8kuo/AY7wZUuJecbJEqFYPwTGPiNbs4gSOVXhVJMIOlVsY35HQ1\r\nPvNCiTL38FJ3PIQppEdazwRI15aU/9JOHjuuHSlXGpIOFKOap9wL3DEhSdFf\r\nSdYpy5yxpbbWyXFBrcRjN3DZCyrlZ4PDoDMHV6Cq5793zWoXTNVS3aEU677Z\r\n+GUejAcKF34pBhNt8XNorg9aIsNFlZr6uTxYdX7gazX/5ijevSiNo+D33LF3\r\n8t1xaV6quy77OV2cqnFXzARtnvFFBUsqyh4=\r\n=LALN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGjRBuaNi9BDLmhrL6ULPinrw4xh4Jjfz2Cvrtn1vkw2AiAK6TlNeiar7dfUwr2t8WcNtK9NLO6N2GLTXGVUWoE9FQ=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.270.a4ae2b45558c80d1261020210a0f21aee4ebe293.0_1645186328080_0.22615303490901195"},"_hasShrinkwrap":false},"4.18.0--canary.270.8b271a3b378121826b9ec8c4114accae66bf8858.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.270.8b271a3b378121826b9ec8c4114accae66bf8858.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"8b271a3b378121826b9ec8c4114accae66bf8858","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.270.8b271a3b378121826b9ec8c4114accae66bf8858.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-BIVNcMWSmRUtkuV6Qq7Cx2GF3AELJ6jFsjCpkIpnBRpoAmsEb6q+Y9RYduwQZOl/I2AHfNQtT1jZNyKBxcYwjA==","shasum":"d893bd98e563db483c193faee87c5be8d23531c8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.270.8b271a3b378121826b9ec8c4114accae66bf8858.0.tgz","fileCount":113,"unpackedSize":2085695,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiE0njACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqMoQ//bWa2Bbbj6fh/UZDBwQwWgx4gHHKJ7aZ6XLBjSYWFu4AnrLjW\r\nCPqo8iWYwWJagx41i044mXsscI2KuOuYbMMly1o2SKnzmv/zWajYzAL3s1IJ\r\noQVmm6uQYazYV4GmbFxXKYXXBjZUd/7VRxy34YQUUa0sSQLpzX2nd3eIjeQB\r\nYF6fuEzTi39mEYvAzPhBe7NRTpyNwgxnPp4fJlhuJgwdnIknqMObPPsFxFcG\r\nZAmT5QFjdf+BXLcO95bNMOyWcVCo9QRurmTh29s9slow8oj1R7wFDynra0ZA\r\nnii7APKBap77nWfIvRfL0aSKCZLd0ULIKSOLoIBnreDc4lpM+8Oa//EaDpCN\r\n4bvqXDVfa06sj1VAZz8Lqa6FSD3cjw7m+ar2Twg+pGBbFkmWAjzyMHzVeuEQ\r\nJQ+agOliNOcopmmWeGYEKXIfZ+K5Lc4g8iy85PzkmTWIigH/8Iodg3oKaJ0P\r\nQqqCN4SUaeyewiZ/aStctY5QecLKqLd+8DDZIINkvwio3Znd6rKxJni+q+m6\r\nlse8pE43PsCmvKWdgjzzyWRCm1/4xygUudVdXuWDr78h4oTz8GATp/XgBYXm\r\n3dEbiAL18Gp/RP4W9/yvVD6BmMEIo2XzTVzqIM9oqaWOBohIHOBI+Xer4+2i\r\n9n4DNnVOCXtgKs92RXhrN48Kx+U2sE4QM70=\r\n=Wl1p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDxKwYdVy2e5P0B8NVB0f853lpuMtm07DMqeOOAh1wP6gIhAK+Nfu2maZlgmuFupZSOcYDkU0AKkTW5Hyou4ZUSiKLr"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.270.8b271a3b378121826b9ec8c4114accae66bf8858.0_1645431267130_0.7586590985014592"},"_hasShrinkwrap":false},"4.18.0--canary.270.05c822724ef7ef295b0f591116777b8da9dae6c4.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.270.05c822724ef7ef295b0f591116777b8da9dae6c4.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"05c822724ef7ef295b0f591116777b8da9dae6c4","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.270.05c822724ef7ef295b0f591116777b8da9dae6c4.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-24sDD94fUqZ7KgKW/UPpIRy4qvjrWrKZjAzttVIxQm9rDMVXyG0ZbBEbU26aNoWFsc7U2ClayOm7F7qhxX803w==","shasum":"3aafea20de65c98110772d4236ba63c320ad2376","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.270.05c822724ef7ef295b0f591116777b8da9dae6c4.0.tgz","fileCount":113,"unpackedSize":2085349,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiFO1EACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoTvBAAk36ob4s3z/9w4ZfjVJS1S7wINev/x6q/SOCntKJcav9Qs9sD\r\nsoB7YPDJ1xjg8pyfxH+R6WrZ/e/NQCkrYSfKUeN9sNWB9pbT1kMltsRB8Ijx\r\nIJTkPnqT9arxaElixN455/MhJ3rcW0X1pgidUhNlp/JH+3F3VBBCbsFsSUI3\r\nEafs2+z02zcf4M+QEkweM9wVd+DxjfNrls+yrDeDF9iNwpwbW3Mql5KTHyD9\r\ntMyUThWHd6guYnwRSUXEeN7nkrnTF+j1iHwa2+VH3SgOgrXVdSOdq6Zw5OtU\r\nS3hhaJEAkQ8nw8jJKD0fP/X/GYVkXn4VoKM1DiyTwMl2nFPziKXwCNaFF+vd\r\nY7SR8TkERSYzjeahpN7pXLPrPeyxcvzxaHNPn9b1gTchiEW1nnaADl/57DRp\r\nepk1Y56F4AHhKH6xUjqSoKalrJwn7mBnxE271g4/2600CQl+qOVPDQiFC+wc\r\ngEvtJ2HMX1+lME78Sd9y6Z2CnPyIjJ8RtyiDqNSyQkzjl/x2Dbwl0SFSvLRW\r\n+yvbmbYkY0uPUSqdWMzbfv8b1Ze2KwPYLiMjvrDxPR0O2SG+kuOsscy0wiaw\r\n4ueUzflcv/QUxeGzKG/aYQcoMcatWd3zWP0rN+yi39KY4L4rZokDMDlXEg5F\r\nwOSuVvJS98reNgG1sO1Jlcz/sIO6e8qTn3A=\r\n=cUgq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC5G5/Z+dFRMGw39A2Y9jY6SlGi3gQLswUpjSstBxrr9wIhAKvswGZhfV6vgXco+lAlFsbyc+7+MJJSS19P4NYDeqq1"}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.270.05c822724ef7ef295b0f591116777b8da9dae6c4.0_1645538628504_0.8501790740062785"},"_hasShrinkwrap":false},"4.18.0--canary.272.1b0fc82fd1b7921c6d8fc238f9d86cd3bd7230af.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.272.1b0fc82fd1b7921c6d8fc238f9d86cd3bd7230af.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1b0fc82fd1b7921c6d8fc238f9d86cd3bd7230af","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод, имеет значение `undefined`, если устройство неподдерживает данную функцию. Вызывается со стороны смартаппа для остановки озвучивания ответа (prononceText).\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.272.1b0fc82fd1b7921c6d8fc238f9d86cd3bd7230af.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-fjz8PtlSI2dtGD1Rk7ilIHv3pmw/2Aywr4noeR2F4K4s4LNEwKkJu2MVNjVp0eHbEjvY5oEC1f00JXw+zqnAOA==","shasum":"5a88b353e3666aa5b4660c74aa5cff8f7808d3c1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.272.1b0fc82fd1b7921c6d8fc238f9d86cd3bd7230af.0.tgz","fileCount":109,"unpackedSize":2085556,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiIyALACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqgNQ//TrbQWPKDvTOcRjtzSnzEmoKfCeWEizInnJtsoKmvLlMkl65W\r\nEsJ5jtP5CEQ85sgCcHIIE0fG2bdGOdkOtmR6B0B7Cg2CtFamekpLtgz+Z/DX\r\nYILJzGAB7+lbnaCeR+gVeoF9wSRIzCnqUvUFrP6pnZ7ZaZtxBal0ftw3jyXU\r\nzqYgKjiDY46aXDlQtyelwpS+BuxlUQvktmp6mvER7s2SH5QGlRgCRNGkcIQP\r\nFE5TjVbTkmxjZXDAirX/e7+QYer0zsAGvmOuye+8ePa9nRUJShHOeKbgFpc9\r\nq+1L6zHmnfcFpgQjh2oX/33fsDrY0qXrKRfIlhJmgAsp8vFWFNhysg/F8Iot\r\noK+1hJODny4Y71N7GXSkl8wIZfOjV8DGhhpPfFA6nTQVia0UGomEIvNvZm4g\r\nanzsVpFldv15vBhQ5HQV3oFt0muZuKHdjs7GinmVA0ZxP1dFe8Yk7PjGVVzX\r\nu+/0y8sFC4BTD/04/DKoR0KuzggHqKH3C53uIVXUAWAUC8PYQ0x4G2w6Bukb\r\nvGT5oPaj0EKvi5XVeL+PhZUgfiKlbFaqNoEaQL8f6SEwfAj8sjua3kpqhUbs\r\nsPoZpuRm91snBsX6npED0kYBBffP4wULIRxrYpGu1CfEsTloRPZwuocbVSg+\r\nv+4NwQwEJyC6CfqYQpMElcwqMhNHD74nagM=\r\n=TxQw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCojor30jrpIOA/DkjSX6zxQDV2shjs/EKYTO/rnRPhnAIgFSu1v2943LTAjJZRMG588HCMLdRc/dV1SutjGBvtnrM="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.272.1b0fc82fd1b7921c6d8fc238f9d86cd3bd7230af.0_1646469130721_0.0665929471888711"},"_hasShrinkwrap":false},"4.18.0--canary.270.ae005bd33c1cd9c8fdbbb8346eac96cfde98b1ac.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.270.ae005bd33c1cd9c8fdbbb8346eac96cfde98b1ac.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ae005bd33c1cd9c8fdbbb8346eac96cfde98b1ac","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.270.ae005bd33c1cd9c8fdbbb8346eac96cfde98b1ac.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-Z6OT/n7hd4QHa91THEP9hIa6imlNRmAdymge/3CqUmjdDG2dFwgJLGQ1OO4mvRGtTjGUZesBxsOpw8cQGexsfA==","shasum":"cb770365f689fa4399bd9721525ab87bc8e566ba","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.270.ae005bd33c1cd9c8fdbbb8346eac96cfde98b1ac.0.tgz","fileCount":111,"unpackedSize":2085413,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiI3eEACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmotSA/7B7catOFzS+P4HmX+dZGUQ6BpylTW3Eo3mvQ23GvxEfvWa62Q\r\nFY3oR8YtF0dmF8m+fn5oYWRx8yyEDDgryFsyAIlbglBQVzE0cuNodOSSZ8Jd\r\nU/jp29tNdXw6hl5O4tGzBjn5wZmDlr+eGv6krCw5R2UCkEKzjvS26HzXODp/\r\nBp7XPw/HoMg1DP1CKjxP1pYZqZ+Ti0DNMyuy4MSBg7c8yF/eYH3CzyhtuwIV\r\nC9tOXeFKb8nldeg7DKlLbGunCsbn6Exi2WpqMVM/mkeOmZJAkczlsZC0fgzi\r\nP/5yW3uAoL48Zk6yM43a6WIW0ZK6vuk5S0+dwvE6N3PKbtQavFwtJplPcdQV\r\ny1eVJ5uN4QLHGn5/K1OZiMxzYUitxddRUj23ScTkEpqxSviuCTewyGD1I+Gi\r\nDUvbUtOxtpzLS9CZtDJlqE7ghFOqWS2nL8GmRjRFCyMiLdUrbU16ndrv6COX\r\nGR1iyHWsnumxIulXwFZo25/tEjE1SiY0OifOHIbSex4cvVfTUKzCwZz3Kg0i\r\nTmzX04WNhwoybbwFsdnDfcpVA5veEDIGsnvWdLCIVKrqwfXJmFAL+JuJ54pr\r\nV8rgQ+R538YEMPwoh1lc3PwbCcESWg6lxWgeg4JTqChV3gZszNjrNHeF33H6\r\n3PlO2xhGe+p4MVYQq89ukmnCMToDdch/1MM=\r\n=iVGa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDLqAA7vjfRaOx+265+HtEmufiW6qxoUf2EajesD/7CLwIgPce6h8wqgXVZcj00k0DMTb4wrO0oYdzyrnbPWuHfwl0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.270.ae005bd33c1cd9c8fdbbb8346eac96cfde98b1ac.0_1646491523980_0.6816448908478485"},"_hasShrinkwrap":false},"4.18.0--canary.270.715d2d3340172fc7abe84de657abca02755c7eaf.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.270.715d2d3340172fc7abe84de657abca02755c7eaf.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"715d2d3340172fc7abe84de657abca02755c7eaf","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.270.715d2d3340172fc7abe84de657abca02755c7eaf.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-X/xnYFVIUGEEwd3xULBcTbTPmIEYmh1hWYus8xOTeKuRdaJJ+R+zpt5J/9fOpS1DtxaPcK4izQEGVqFzCTecaA==","shasum":"f747ebf3a0bdcd912108a84fd7289dd929a396a6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.270.715d2d3340172fc7abe84de657abca02755c7eaf.0.tgz","fileCount":111,"unpackedSize":2085425,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiI3zDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmocFxAAhA87jeQOARiaZlYGALIPWOCK4PtGB0hFiWXfzvIFU2L0r5yu\r\nJPrR9L0gJPMnm/fqhcM0ZJLkXXbuerVriNQIxIDr/t8TmJRG7UYRDNQGyorl\r\n8sij0l8V2vQyvoYWlOymk58RwKsL4bhjn3MgdXCtuZlDbgSAwSTyhCjfUBkD\r\n37PbRFtavrbs7YqvwaQLusDeONTN8pNez8e919ESFvvO1ebECoCy9A9Yhml4\r\nktl9KrD2sWfTDN/C2HHQITXbe6MyMMSAXIQcybnb2ov6+pbRbu5CV5CY3AGp\r\nw4y9hf/OTdRPxkkp3ETQiQv4lIFb6yhWBSRmaozwpvPBdoki0PmTTgVh8PmF\r\ncy2gojZPGXyBD7KtIdbCkoCEN2sVfXWoqYpTsAt/XD377IqWa2AZArTtWcBB\r\nCsG/sxRwiLbtEL5Cubti3+eKPjxAJOJUfGcFSF+G4H8sBh/ZMPSYGZonbxz7\r\n4zzq4Lo8dTWTfNsuE3cWRQIvo9zLGBjTvn5cNmFAy/+vGtBbGbUvZsLt7eTq\r\nppEAm2IFnLL355CH+BQAPRwImD1ZCvAMpNzg9lsPXZ63GUNvu2K7rGfggmAr\r\ns3h3oangWU+m0rmaipyz5ZXC3hE6JwRr4qiCfiZLQVmuSqKJpj0OLJbE1qp6\r\nWDYgKDAflAsUMaTCixUgsWj/C7Djazd4IR4=\r\n=XO7w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDiK3Kr9VeBviCU3/I6Lquh3WCToOp9Qpw3+b/E05EAFwIgO8P/H2N63DaDKDPRBI4J/SGbZ5MjDGRkL0RgR/by9tY="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.270.715d2d3340172fc7abe84de657abca02755c7eaf.0_1646492867473_0.7731715638829881"},"_hasShrinkwrap":false},"4.18.0--canary.260.4c2c762e7827a9a32ecfa91476e7139ab8a2a719.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.260.4c2c762e7827a9a32ecfa91476e7139ab8a2a719.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4c2c762e7827a9a32ecfa91476e7139ab8a2a719","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.260.4c2c762e7827a9a32ecfa91476e7139ab8a2a719.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-3JVeB/C/8v1R0Di8Luppsk3inrds2wB92x+3v1KG/n3mGZM/Yu5H4t4Ewrdu+INSnc+EKOMUatE7ReIQCiCTQQ==","shasum":"2e6e4fbf06be6ace41e630b021ceae52e5b236df","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.260.4c2c762e7827a9a32ecfa91476e7139ab8a2a719.0.tgz","fileCount":109,"unpackedSize":2084994,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiI7STACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrAFQ/+IhR0m2WfgxwiyDI3yItrBjCe6PYIHpwmBU3KNDN2qqu+Hxrb\r\nBWpFAgRz6WGm2uZX7X4fFl24NxrkO0dbobkmZ4mxY8kEcpF5sMIAizVVYfdv\r\nyaFxH6eq4KO9v+0XDfYbGpUdNHcv/HVuKLMupqQvX8GSppT1+VYSw/GQrPo5\r\nf4eRKCQJ4OSaZLg+yPD2CRxtHFmJ39KTyIlNMQa9yEjWB4rVkBU5GCUwMPXt\r\nGdPMSGNdkaGbX1DveuUnoUrQdWrXiX7PehCfnIO6jnNX0UTaHcj9MoXXauiK\r\nVlfYIUalNnuH/EhjuyFKvu9MbhZ9ab0VTEyM6BdvuVJn/2wyeVtihcsuXocP\r\nlHvi6kF6Ia/GDI7c42IJaCgXM8MQVUPc6+Uw4jCHo65beUcJ6DvF4O8or8hJ\r\nUnh9Njuaps24IP9srQr7xOnr3XF18vLIchR2A6O0uKGEhFCytPn1xyT1zlNk\r\nd+VoGWUF+nCi+GSx3/b9LWBOnzCSeoPoT73Qvp12/L3LQYF+lQeEIAjFrSMT\r\nVTp6JvCu2dI2sv5ej9r/NRbRp9Ve6X1XySSTgIJE4HMvxFTzMepjQY+z/NwX\r\nfPmE2HKkX980Rd5HbsBMrqlDbP3jFqekabmGNndjsW44UOtywhNpEYLR2cpg\r\nSzcVFgIN0qCnjFlbDp+0X11NnqMQjUBCaeY=\r\n=sjzJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD+beKjivNxMGI5DsUtfULnc/SmcqlbOGXhUX2e+6nk0AIgElPrkAUpcbHAVc216E8GDam1IeWtq0jz6w+OToY5Kac="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.260.4c2c762e7827a9a32ecfa91476e7139ab8a2a719.0_1646507155231_0.48563847122588544"},"_hasShrinkwrap":false},"4.18.0--canary.260.f667de7326ede5f30d85fce9f01a6afe46b2a5ff.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.260.f667de7326ede5f30d85fce9f01a6afe46b2a5ff.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f667de7326ede5f30d85fce9f01a6afe46b2a5ff","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.260.f667de7326ede5f30d85fce9f01a6afe46b2a5ff.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-Zo6FrFW1zfEDsLpfbAZt/zBDnl6VsHKcksU4CBBfE6ot1sKShLbhLyII4BoTNv9+lXd/H90kt/iiiJ7b2IXbdA==","shasum":"5520900ea80eb2a16d3f3fe5d39e62466bef1822","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.260.f667de7326ede5f30d85fce9f01a6afe46b2a5ff.0.tgz","fileCount":109,"unpackedSize":2084994,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiI7TPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrraA/5ATuLDX1iZ+6fedDw1blf3LjXFhHYWRLyA+3h70f7Oz0mognh\r\n89duCwiVBHsqyIpgcPVlQ7+GEn+w4KLvrvB2dqEsKFJNHqlpveSbv64vcHWb\r\nCf9lfeYsUaxC91Fe7QcBakuifnqyhPxEu6VWIevt+UnEIDG7PatfMW0FNMF2\r\n45GpV9j19fMJemr6j5pJMh2ToQkJeCRrmUmnIAPHEnxwR8vZr6L/IS6rW1SX\r\nT8+92nGcI68K+VfqlynVRYpBMCATfyGR2HVeahs82T/AiHRqaI6VkWr8UUVW\r\nCSd2eI9cO3tvx7m1lBdf7EdffA5NPOCwY1dlH6fxY3a8Ob/Bi78vhInReQ57\r\ngBx2ecIb5zVHT6G13lXRp6CYgGGYnCwf+GmS9dy2iI2tAM95o068064Aa6/R\r\nHN1LjP1dqYGfkRynA80u7Ayj5033C5nRcVhSVeWAKVmeT7t14U5o3bVqoLnd\r\nvYYuj0Yf9EzOfbf3hRugnYsqLCUdybPkoUQ/44bIyLvrnrE+Pe839WlkhULM\r\nsGEwV42IyjGinkFV068ePXVoetiZbmCAAZkvhRyF+z+9lolrlnElA8vu5jVx\r\nDfUjS8+e0j2zp0nS7OlKkZIU+qZRqwtxJJmDxRoTLKnSZ7Pd4d/x4FLm0eeH\r\nec6U4Rv+eRviS1zIXXQyDc6yYJATd7SR6z8=\r\n=gGj4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCkbOIsfzOgiNNYnbrF4250laTIvW3VVb0tgxu2IGcidQIgUF9N5RzTe1pgfEeDyYMKPjOzatTt1nxNIi3t9emC5zQ="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.260.f667de7326ede5f30d85fce9f01a6afe46b2a5ff.0_1646507215314_0.6566651423698602"},"_hasShrinkwrap":false},"4.18.0--canary.272.a5e3f743eaf476fdfa94805f4537ba168de07383.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.272.a5e3f743eaf476fdfa94805f4537ba168de07383.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a5e3f743eaf476fdfa94805f4537ba168de07383","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод, имеет значение `undefined`, если устройство неподдерживает данную функцию. Вызывается со стороны смартаппа для остановки озвучивания ответа (pronounceText).\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.272.a5e3f743eaf476fdfa94805f4537ba168de07383.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-E6zEDcZmTTiAfmIUQuzjdOBZb4ee7+/9JiwuRusjYmDXpCbiJ1ri2+5jJeXTcCTUnbt0NNSlWLMzptd2oZC6Kw==","shasum":"23474c1a70fd5bf4a31198389b8c70afdfb63cec","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.272.a5e3f743eaf476fdfa94805f4537ba168de07383.0.tgz","fileCount":109,"unpackedSize":2085557,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiKf75ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpiMhAAjvMZWWt75qY3NQbppXwXyVJVJMaK9HkErTfFPgwfkupYObbA\r\nDCB+HxaZqu1K0Ot4UMfbHiIRXKMbIf+9RvTGU8iO4REP01S83jHW2OSUdHYH\r\nuTD3vMGARJsi6NUGUCnk7YpBlsyisyjbRjUuWnlAWJ0ObABKmKcsS2ltJ6rY\r\n0tyTYPjQ3mGUNL3A4SFolHgfmHjdbKTQyC7j1H+5iTBtjztrzI1HcOzvqV2j\r\nyKyy0z1DnUA54XBMvFN7nrd1NjiGZWVBnFtDsbI2ZdkVIjkG5m7WEWhC6QLn\r\nZvTQ8rQyYKlcC6qKw97dbxqHWX2pbhOJP5BlFEtJnnq9iqezRFiCd3/KbHlN\r\nlQbtqap6tzyJjRhs1pE1vWeFiZY+o2wGdAmYGf7tNvxmqF2khuIfnT5OmFOZ\r\nMbDo4F6Xhpm3Qk6LYkfuY7rijBAkeRcBBoFItrqxqL/owhFL5fI0tpMnInyd\r\nklBAAkYYX5GjZRXsovDCR5pj0HTXiVrK8FlaoRvjTXbh3OW2AAxebUgkrJVf\r\nxTh6YbITddOA+ZMe583mhFCX12feQreRQ2+3mh2Y8rlUvooLC4SyIhrIOzl2\r\nc2m3PeqCxh3ZeBzxdB6Tgi7YlGNYqH54iPultfI9zBxkbOVGTk2HRROQ+0I6\r\nFXpLBmHZkx7Y42oE4BypCbMZTmhB47GzNG4=\r\n=rOCJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGUc6kmz5wRDqELKLQk0jMqeUkT+FSiK7nOhT1z1kt8PAiEAjDKhUNBOp7FnrBBYJDoUHK/QiCt0qMym44XOwncfmks="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.272.a5e3f743eaf476fdfa94805f4537ba168de07383.0_1646919417589_0.380318524140717"},"_hasShrinkwrap":false},"4.18.0--canary.272.f748c7ce92fa979965f272aa85a31ef70da3d4ef.0":{"name":"@sberdevices/assistant-client","version":"4.18.0--canary.272.f748c7ce92fa979965f272aa85a31ef70da3d4ef.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f748c7ce92fa979965f272aa85a31ef70da3d4ef","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0--canary.272.f748c7ce92fa979965f272aa85a31ef70da3d4ef.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-BNLGSONuhiNb421qoCFwJ5npgNDqxrVwr7Czg17nrynHGRiXmBAKya+RzBKQ4qX8uxgEyzoLW5RQ3cD7kXEZ+Q==","shasum":"a2320b6b0bf634753962f27ee35dc1dbb0feec68","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0--canary.272.f748c7ce92fa979965f272aa85a31ef70da3d4ef.0.tgz","fileCount":109,"unpackedSize":2085554,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiKykHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq61RAAilfJ3dsXNk4wPhVpDZnuEThPCYbP1VQEPBYInaPiWh2NqYCp\r\nO2k0KQ4IIGx5EXVqIgC2K0KliRq9wq8JrCCGwk5/sCbImlIxlvuHMYwEWSnW\r\nImBCdxQN2oswbXu59MTaiVRROj8AsccUY0FWYkLV1Chw9qU9iyeDlXgllcoi\r\nM2Wj/XWjZPqHyJxL3a/WPbzJt57Z60nE56pTDRmcfS507uUSipKKVZ0/u0CF\r\nxiJKZWD9PO0cnmkch9uBWKKx7NQa7numZojbq/B7rolO+gBd6DDscugK/zFL\r\n+SqHe821u1UVg6SE5hQTsENj3kbuIW4O02/ebOtcu//fDjB5O4EW8sR0PH30\r\n2v3+axr04Y973NV6bmztI5SBHZF7KGKglLwDXTYmixhIQHkh5Q2Ga81lkkRM\r\nX9ipG7vzBHRhJdEiytF6bdQK8WigTBxZDNyBh+oA5Sf4BKSy3RqOvnJ0icuY\r\n9rh6oVsWqrqTz6ZlQiXIK+DlK7AI0LMHpDgs+GiDWqv+LP+7cTxQ34QcGRal\r\nLclpN5EPuGwFySGG0P7M7pantmO3CL+Nli9c09TaT6CClgk+9AUurchzJpWH\r\nTVBGlJDpBxUGDUfwnjfvIvgRLy91p5LSx8TuoffmfQrf1VJqIOp8C+9zGqXL\r\nr8Q2wlPUBfzMVsdRrs8cN5KVDNTEJ75t35I=\r\n=k/w+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGRmT5gtptEFEKec7W2nmQdCWr5JrdEluRTfEMVVH0uDAiEA5UUtt4S54q+uGfQnZapazgsZX6kLs5aHK8mUSiJlJp0="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0--canary.272.f748c7ce92fa979965f272aa85a31ef70da3d4ef.0_1646995718876_0.1733154539843651"},"_hasShrinkwrap":false},"4.18.0":{"name":"@sberdevices/assistant-client","version":"4.18.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"4fbe9b6a0b44fbd7de7846458440bd0987238f9b","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.18.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-+cmpPAIkahyJ1Hmg4ljYFbN2aUlLYl2X52ZkQlafkFc1wm3zzX5v6/XPIKSxvD6jTWnd50B/FqrJiJqmwjwDgQ==","shasum":"dca562bf43e38a0988a2c9961dcf1e03c70aee79","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.18.0.tgz","fileCount":109,"unpackedSize":2085427,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiKypLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmruYg/5ADkgUBg0XxrzAVWllLAChL+4SnSl+4LqCrFLxKbw6Uyf/yDa\r\nnhJXIeBYXrJ8UaXnHf0py/+vBniq9xAKTDBND9FQ66w6pyqCiDKkugKc0J40\r\nNrO5kICYfRDtU68dQJ3mYl9cgwsriPcSs0+m3Qdx3RlH6h1jFXgmMoRWMZ9B\r\nQmbwE/qEHHwYCox6rtnzQehxd6tXTnkvChGJZk27Wba2PYCtg0FWSb6QUtly\r\no0fQ4/rXBezZSrZSMlmgwXMnq54BlyR3yBn+cYQOhFKdA7swYjkKzrn2zxci\r\nbnY6ZZZTeXsCwd+0Y5qQ1OkyfdV2Ttl8hiPCfX5syogwTkiu8PlEmW/ozq2Q\r\ngaGqN42+U+wOL7DU8i5Nixg1rBonVjY7acGPD26qRpjQmZDy8CpqeOxxg0c8\r\nXmQ2iAvsl+3t66jI8qmyj+t1MlGlUYyZdEQ1+YauaXDKR2iqaJxPq0GWHgZj\r\nPRfp7wtBgtXPt+fEyQLJ62tUny7bYKmwjudcUcI99GLn8JgDlsFvzE7Ydr5+\r\n8juxdIOIgaurNYlKV0AlUIE9cPGw4hRVfsuBOmpbV5+uQnh2h1feJHiwhS3h\r\nOr+1SBd7OcoGZmIH+OuxCVplSARSlz0E1NEg8IafwIWH9slaoy2AY/0iV9SW\r\nTLfM4QvpA72+F9UdaHvU9J0FqFkFi8GZCNc=\r\n=HMRA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBAHlYCrc5T+x3WVODrK5cgA2C1B80MVggtg5vt2kD+hAiEAjuS19y+uqamz8hTeCLFn+dJ0hHuKOlgRzJkQn/YlqAg="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.18.0_1646996042896_0.9949220898239073"},"_hasShrinkwrap":false},"4.19.0--canary.260.b76e5c015ecbe4a30573e00afcbbe983e2e209ec.0":{"name":"@sberdevices/assistant-client","version":"4.19.0--canary.260.b76e5c015ecbe4a30573e00afcbbe983e2e209ec.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b76e5c015ecbe4a30573e00afcbbe983e2e209ec","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.19.0--canary.260.b76e5c015ecbe4a30573e00afcbbe983e2e209ec.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-Dhx49N5Mz49SuIOlQItCntFHT1VX3gCKCZu85UEes0Hd2O8R1Qpgm5AqLTm7sJYB/1vlQRopGyxH8PVLogE2SA==","shasum":"1ab769c2190143be03dc41f38677361eb314779f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.19.0--canary.260.b76e5c015ecbe4a30573e00afcbbe983e2e209ec.0.tgz","fileCount":109,"unpackedSize":2086744,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiK08fACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrjrA/8C59ImHCaXEamd0DCc5f8zL+pJ48a/uO5LO3WCvcUICRwbXEK\r\n9RthEM+zPo951B5rOiBnDlQ2vjamJVTnV+SYPXSSSgse3bqN3srKDZS1AQdC\r\nWouuemCO/AaCtBySg1Karus/umJJDLQMXjrG7nPEDG9pGvbTv7N5vdAmYpI4\r\nRta8r2/kDI4IDllWmpcK6jQn+rU5Pg+BYXw5mhNdaAmVqRJ8XEn/OkZkWYU2\r\nobMtNJD3Fwgw35pJyb28QsAVyqNMCPiRVAT8CZEd17ci74zs6Wvrzxw243Ly\r\n68BVYxWkO16tk5++FvTTISCXCZQnNAvAkHe6g92LhGSwwEZ+KJd41IfCYs69\r\nhLPdS6PAxEZcRyicIjT70jJ5LFI7daWRxW56vQcpyJ6okrtdqbde7GqWeK8F\r\nx4S2DemGt8zAfpQU2W85GSEZiLC1ajyNZt/ktjSeJvZg+nO68mEuGonK6ZDt\r\niMtat9T0Z0cKNW4n5XoiudNvkxZyJhxTgheaGsGDJm+pUMEfIPfzszkPlMO0\r\nZ0eV6aspmi1Cy9YRdNXh7G6vdo58LbXeL47Ag30D3VmFIAMFd94Srz8YUkvy\r\nLj8Mi7MA0txASFnznJh3ydz9B99AqNCc/on1ddDNGPCRXUm4xrBFgIi8i8/a\r\ni2A2LjcEeGtBl3mtu5j1BhP03iGA0ZhhLRo=\r\n=3Fwi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDxEVY01DN7sahWAXwF0z+tDGS6IQIJpGxYdGe1UuuCBwIgIs4FFZmv54YYAmLtMtJpe6jqnTpDRW3NoF8Uq8A/ljE="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.19.0--canary.260.b76e5c015ecbe4a30573e00afcbbe983e2e209ec.0_1647005471599_0.5101126509623306"},"_hasShrinkwrap":false},"4.19.0--canary.260.d4cacc7f74843d704f70a1e08b370810be8fed17.0":{"name":"@sberdevices/assistant-client","version":"4.19.0--canary.260.d4cacc7f74843d704f70a1e08b370810be8fed17.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d4cacc7f74843d704f70a1e08b370810be8fed17","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.19.0--canary.260.d4cacc7f74843d704f70a1e08b370810be8fed17.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-XYTSeTt+XGHo2WZNYACm1lrqps3Ud3QxTCYjQ+0pNWTN9Rvp90ARaZsJlkWfVbEFiUkSVTyclx6wmXVLSXYJgw==","shasum":"469c2d80d7eb8cf0cd5637f4a445aff7f9f4e0e0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.19.0--canary.260.d4cacc7f74843d704f70a1e08b370810be8fed17.0.tgz","fileCount":109,"unpackedSize":2087095,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiK1VdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq8Nw/9Evkx/XM8DPBQkOHcFTMNkEUsyS8ScyrVjC+vuzCu2kVer1Sa\r\n+DBSdOKPAFkkyux7WepxyM+MvCJoqXR5ICRzK5J6d/XwFNztLh2nvapMomsC\r\nDCIx4MuOM8qKA3l8hi/XRB1329t6sm4d4Zs152nTTL3av9PlgmLLfGVmCgxB\r\nv/evT0rKFbASPtlKeBbPaqSCo4zPQGG+loDyKPIwQT1Uziv8BrWg0wWuW8GN\r\n0BffYUdfWSnmy9kDmYWkmtiz/1K36xzmwX55MwueWKLylpMSh8+pMdpFeAFy\r\nfvA3cv9aou8FR1SKTMUvBqeaG80DZZfI2/t4mUJI0dG24rsasmG3obR4AlKD\r\nrxrgkvYIWZu7L/ZoEyN7PprK3OUYrfGMKvdjYSMaSTTIIS+EYyokPyCLAbG5\r\nBV5lSgyDbmCAXp4YOMjlZWwd1Sf5XgJrj0ULNPsBUhMcKpDhkw5Yk/j7uAg4\r\nYHqGeA1evbR6gUixaXzmCpUm2R6lT4b07ycW3L6rsHRbzDcMwbss6pxcke8y\r\ngCsWxM1Uoo1vxfzP2j458BuK7i0ukpTwVyzKjq4EndINnEvVedYHOgMUNopl\r\newghfpzx0GmV+1dTNsZLk/e5st3A6iugTpjEopfOojDUPeuAYjuV/3/hKElM\r\nV2oyMM+c53Vy4DIfYAEbDUOsvb3J1diSKKM=\r\n=tB/7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBw2O6jfh9I2sqP10hq5RkasBdT423Ol56DyhnU0Gj4QAiEAh1Eod8Yu0Sd0XBFwR7/QhALqsbMk7neCFbiLOI7TwgI="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.19.0--canary.260.d4cacc7f74843d704f70a1e08b370810be8fed17.0_1647007069057_0.09995197901551611"},"_hasShrinkwrap":false},"4.19.0":{"name":"@sberdevices/assistant-client","version":"4.19.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6335baf3babac652524ae7544dfe499660cb42b4","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.19.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-j4lVVjvhWIsJQq6cclUJstQYWXvEbePE3vrzd5qwnDO+vrEGI2R8Jz3GSpQAmqrjqwB+s1U2MqLekFlurb7H+Q==","shasum":"27a6b6111bcfb1c4ec84d9e619754e32ccc0008d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.19.0.tgz","fileCount":109,"unpackedSize":2087028,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiK1hCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrRCQ//eQVWEdwX+x+UpuYd18mt0OXJGrrDJHL5oEg9PB55SOL0tQRr\r\nDqpP/7AhEIRn337otQ1OV5JPLeklrrHqdaAgTTxI9+Poouw84TU1afUKKQLy\r\nJEnNcYM3GLyZsLPHKsIUwXtHHYrqotvo/lO+lQLNLbrdubEGz1zN+kRW5qET\r\nirM2pbY9jjmb2aUV0JbEx4Rao1u34k8bpKZJ3Kz5ibOif1O1+OgeYZPR4SfJ\r\nLnWZHb524hdGQ6auHoTXky7cdYpA8KfxmHX8P9TBuSTG2jRxzPVfUZrIylcI\r\ndQxAaKNKCmyLtNMBeVw3OtFZhvhjCnFilmkJdUsWbX1MXzKg+C8Z7PzV1VFb\r\nkaKlcf7kQWMpQpRgDZXX9s6WIj7x6YFyP9h5j2dJVdY8rN34LACTQ+8PWoh6\r\n7eKCQYX+BKp12eRm/cEjuvc0buNDBAh92tnVepDbuMgNsTVofT66D0I3i2d4\r\nYKD4OL2z8iLO9jYiMsDR2fCXOVki43fwjGbROM8jecTJ6PhoyMrWxGMZWAxs\r\nPJhn5wKe2L6YLe4xKom/lZXg55E1E8quVjlaWWAVm1vHym8V/kuAFRgLmZI6\r\ndGv7DrDdNH/GvgmOiMs3oqvWDaEEdnp3osu1A9uuzY6DpOmshXs1sMDMlItR\r\nCWRyBrtsYaEH5fbnutVbVJDnkJ5yoGYKi8M=\r\n=5+NW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDvdAj2cxZvlhTnWn96jydU9FajjR7jQl4xWzhiZ/tsCAiBiMfkUZcX6RkVPhcLWbIQjFVzXRKHuZLJYPbD4VNxxqw=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.19.0_1647007810039_0.8234908339572871"},"_hasShrinkwrap":false},"4.19.1--canary.273.cbb41d41920b77f2570a8e4091de900bc17fa2d5.0":{"name":"@sberdevices/assistant-client","version":"4.19.1--canary.273.cbb41d41920b77f2570a8e4091de900bc17fa2d5.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cbb41d41920b77f2570a8e4091de900bc17fa2d5","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.19.1--canary.273.cbb41d41920b77f2570a8e4091de900bc17fa2d5.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-D0eqcl0/mQKTQBZyPQsQLxDSY8deAeAy9MqFeZC99xmzRw+Ucbv0hcT2ujbiS0+U2YgKV3ciimViSLvBy/oDmQ==","shasum":"5d78393b58f78bafaaab7e4726fd9c471e60f3e9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.19.1--canary.273.cbb41d41920b77f2570a8e4091de900bc17fa2d5.0.tgz","fileCount":109,"unpackedSize":2087992,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiNITfACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqkJA/+LLVFU4991unz2FK8pnrgOvFo57rUktAohe6cdQP9qTSGviHs\r\nsRrNsea5johMGh+D6NaxDAtXsJ0SkZtTEOL+RjUDtZ1gAu1A7DIoxm2/ECPA\r\ncdpMCuG6IYcZ3Te+jRwOZCavs3oyeT058QvYYbSm8iLP9nozyBxRNYjAtDTZ\r\nSGP260WnGnQCox9VGtI1fjqnb+cNcUdCxvDdzGl8RnJDs896/wbM49okRS0u\r\n9YgZ3cLiIEEIcPb9VC6WVTouWjXNtJ3QZXMDFVYY5rcNy7A/WrvJjG+pF/IM\r\ng4pHRavn7V94OErktCaL0s69NcHv4OlW2MXy5IsTBEDwd+IC/f0lZGeJSo6F\r\n3XjZlDRClXtrKN83+YU8Qa4HPISd7wDkMVODEbVZA5I+xud+n+UZ4s0qnvnJ\r\nN9834lGLtJ7zHfEn/iLigwYbaSSDO8upqrutVvDjM3EEcJ4ptgOZJ4GTL2/b\r\nXOP3GlnpHJd4wn+2vUK8fIOTdHF4z/4m7JQKtamLXk8bfYJK5falemocHlx5\r\n91iZ49yz+w3rWPYxa/5M1sWOWRI/JQGuQb5qbytfcbrwOP4x8DyvPhy6nrqq\r\npy/1g99Sjggt5mrHHox2T1CZHLoeD0corKLLy78lNMIdFZlov38DXimBN+ai\r\nOatFQTqRuZhlfJuS54/Q39OxB9/qcgfVMGo=\r\n=Rnwa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDRA4kvRj4zHj+W9BQisVTj2coalt3yX09m8q2RJ2956AiA84BNqlYAyZ113viWdW5OodAQPuDzspZ2O9HobOfIXag=="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.19.1--canary.273.cbb41d41920b77f2570a8e4091de900bc17fa2d5.0_1647609055030_0.6037267016829082"},"_hasShrinkwrap":false},"4.19.1":{"name":"@sberdevices/assistant-client","version":"4.19.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6394da43e70a16d2510b82fe52d82cf6342ee1d1","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.19.1","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-Kf1d18PA5hf1Fxu36QfmfYMwZ1PH2CCRpTjRfMTDZ6rG9SvtGOqVOCMCBlghwfJzYPWflrbVIXyRy9KnGWixKw==","shasum":"038ff4d93661263dac2a357f6eb312483596cfb4","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.19.1.tgz","fileCount":109,"unpackedSize":2087907,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiOCyTACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqbbA/+KIz5fz6WqB5O2qjRRMF/6qIY3Scld+NtttkIADSjXy0z9xoK\r\nY4+ST6VMoXIvimvuccoZtKdqMnp0iRGSZs2VHEhn1ixHhhTk1N1T2j8BX3TE\r\n14PS2TvDzRNSWi3bUvhNPnqoktA+JfqthEEeF1KzGLg6H+nPU+ojaocjy632\r\n7PYqx98c+tRX9lqInYHxbUDfEkfJjhceewxbnWzGl78rLEy7yGS6OfsY+cGG\r\nRCju4nyQEohWGeAwu4r2wKkJFilKforR7cLHPNFNUllkTfCWnqa0kkZrHpXu\r\nQcII3TVVEYknBVumvN0MlgyEnHwonyE9NfykXykX2yfnj9v4/aVSi7bj8/ge\r\nrdjyrRatQ4usr2z7TTtMPQKj6OK9WY+DTrm0/3evEz/P/VEjNKHmyOqYy6et\r\n0qfM8edm2tW1tnFYsTUJt880a9hZeVhARY/vpdQCOIS08PePugAZTI6h2X/N\r\nD61HlPNdVYz7g+eQyGf7gdh8mV14Y4RR6arFFC+OGVeOs8IHApcWjLJ7Aqbo\r\nug1LsDmiPt4+K5iNDF4D06TiIc6g4W8MXoPVTeEVCefhS813oD7jV9/E8AMa\r\na52F7+TPk4pixT+2YZABd4Rc2fzfOGDmNzHnAp42jsvpSqMMx5LyiPeUVPqF\r\n1AfCOTmmFLXfSlgCrjfek229ZKzFs/jb2DI=\r\n=emIn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGTuWdhAgPbCKBtwJmPNMXLeZlkJz/SUhV9lvAUP2dcHAiEA7rFcKasncKpyzuqwr7Xd+YuBIeplGP+eW94FrF4pkQA="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.19.1_1647848595544_0.250344504866711"},"_hasShrinkwrap":false},"4.20.0--canary.275.93acba31430653fbcdb3646c34fb8fab29cdf935.0":{"name":"@sberdevices/assistant-client","version":"4.20.0--canary.275.93acba31430653fbcdb3646c34fb8fab29cdf935.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"93acba31430653fbcdb3646c34fb8fab29cdf935","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.0--canary.275.93acba31430653fbcdb3646c34fb8fab29cdf935.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-C3laGdS0Zp/yGsNVJWvMrHZJHp+qgSjVzpbcxb0Ke6ZWb8hYLz322Ls8DxPsUo//beSSAH03uq+8FCFpRfDPmw==","shasum":"3c7f0ff05df196fe59a3534f39f8dc70d7363154","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.0--canary.275.93acba31430653fbcdb3646c34fb8fab29cdf935.0.tgz","fileCount":109,"unpackedSize":2291172,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQXBPACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoGQg/8C/U5Kn3PJQd6h8ZDqKnIdniSaPx4KNIPHQocQsXR9R6BoP98\r\nLM46u63xBnrOvJPGqEytdC9ikNuOs34UtHJO7Wjts7UAoIlzHAX8V2Y0CFta\r\nm7Co9F69Vc8m87lUbEHayGYizjQZaPYZIbdCHfjRXjXXhp1WHPxI8QW3Xyw4\r\nzUaWX3+rxxnI8W1nUN2X0UlOqJ0NYzJN7PeoDtGQx19BAUY8RCXz3oLCIgMR\r\n6lMassNxowPCodU0NiOyqPrDmDgW+MbRj5AHEfMimWXpCUTaNxJnXQZ/iIOI\r\nsyGO8l4kva0G8aQ1RPO6HsvT0fD3kxmDx0Fjw//Y1ROzjckl/2kOJQYuWgMG\r\n+12zEjgv0AxkrFOlNfY6hlSgtEj4U1mFrGjnMUEAJ8U19LZRSNEqqvBFQ4rU\r\nTCzpPULVJ/xoSXTYBSTEvCxL0oer+jfLfLXf+ZeSeb9zjtD9N0i93B30n7X8\r\nKLO2WoZQThFPv2lQTrN89WeWTixoLvn2ttn4ilDcy4j5J19E84cafdwCpEz+\r\ntmYQAXOTifrH4nC3+CVaInAhrAgxPVSrwNPozgaAfeo6oZyA5P02nlNnUTVv\r\niZxOp3H82XoclV6o5iE+h7x0esKV8opKg38tGaks1h2tfFTc0AhZHmg5oZJ6\r\n065Eqkqn2Sz7ZAVWTm/cTtPWhlIiqwclE4M=\r\n=9xaN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHYeAPcsNnOz4MfCQAXvBfkyfQRqKalIf3i/uAf9F5CZAiEA9Tu+obrcVPqZxLcIAGjNkmYiv7X597Xkid6b7ZoYFE4="}]},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.0--canary.275.93acba31430653fbcdb3646c34fb8fab29cdf935.0_1648455759574_0.019396343924217296"},"_hasShrinkwrap":false},"4.20.0--canary.276.2d9bb95809576d4851cea1df48e57c4d48c26be7.0":{"name":"@sberdevices/assistant-client","version":"4.20.0--canary.276.2d9bb95809576d4851cea1df48e57c4d48c26be7.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"2d9bb95809576d4851cea1df48e57c4d48c26be7","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.0--canary.276.2d9bb95809576d4851cea1df48e57c4d48c26be7.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-beqe+pjZrCjfIoWd8pkZbdSqn5ADQp7qwkrxyhceiuGbA4BornQDvkPNtObRJJX0J4IEcfksK9ydIEBpD/z1fw==","shasum":"efe13d01aa9688a7d310e0dbc646a1392083f1fb","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.0--canary.276.2d9bb95809576d4851cea1df48e57c4d48c26be7.0.tgz","fileCount":109,"unpackedSize":2088715,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIED6hLaOPnrJ8GWhz+UcBzIEtMFxwSMDYnbxQUSmrjGqAiEA7T8XvxQ+zj+qgR0hnxVtHUqcoMi3zQy3RMlK6+6gyhc="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRXj3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqWCRAAifcedwL0hU7h3lD0uolFk4Ybpgt2SC2t9/8gupqvYg8qe6H4\r\nW3tfzhIw/O9Ij8CZ0dfcXt3fc+hvmt6+7M7/gKWCSwlwWmjcKVyuC6wQ66YM\r\n28kwZ66QNgV14Mp1SG3X4X4phv3GN+8JmEO/hjF9MEWwcdBjSWVeRGH8BEcX\r\nYt0PMsUlkd6v+hxaMuBAUQ4+32eMD4APNKd6YfqgFgI3azERa0gHV9tygUhM\r\nBcgpv4ZCYxoEBTN3HuyGrnvov+i7Mx3Kqy8jH5Kfz2EGKbDn29cwQaMc5Uli\r\ni5jRT0Iw37obokLGxWGZl1dT9/WkgVAo7Cta7wW0eV6JPGvacp+uo3QtCDjm\r\nmNSEysoMU4eYEZIoPDI0wy2ndIbfDm5EStphaQmIsot/+ViQg4pFReIesZvm\r\nZdYWMIjJgqV5aBIirBQWYzFGuhxOX/uVKA0IVO3wU9Y4Kd5VxcJHJQ2UApmB\r\ni2fJV7LkOXzbxXqYQZArceoqPJxNop0Bc/WyTIligBuNgzXaItIfqaMpliko\r\nHfOK7dOQTWTImNPzmwCnSnt8JB6UQYkNGx2XJIjdBbrWQjWvgzRmPiyEP92A\r\n/bVPTkGkB3ALsLTf6UC5vdveigRfJtbuS+qEAan18B2QgBpY+tFGi5EGCVvC\r\nlGFuSyR1XUSZBJMsmFxbE767J+NOVTL9dfU=\r\n=27fl\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.0--canary.276.2d9bb95809576d4851cea1df48e57c4d48c26be7.0_1648720119181_0.7359699920971006"},"_hasShrinkwrap":false},"4.20.0--canary.276.19be28fbea70121c84abc90c79eedab816960744.0":{"name":"@sberdevices/assistant-client","version":"4.20.0--canary.276.19be28fbea70121c84abc90c79eedab816960744.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"19be28fbea70121c84abc90c79eedab816960744","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.0--canary.276.19be28fbea70121c84abc90c79eedab816960744.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-my68IMR8aVWPWh1LnwyhM87UoU9/X8qJd2aI+vrzbL7TnfhANxmU+h1H7FjmOGgUAcGN++jXLkHog/tWz9DVhg==","shasum":"27b915ce02c5abf1ccbcc0ef96dae6774caef97e","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.0--canary.276.19be28fbea70121c84abc90c79eedab816960744.0.tgz","fileCount":109,"unpackedSize":2088717,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAkmp3uyJ6u7K8iDkRA/cd7jtiZeYD1Sk03lPXcl4VrAAiEA1F06qMymZ1huq0l1xbhQrExm4MpofhiyoxGRCTIK5Fk="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRXp6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmogqA/9GiIRBwWTDB7dNirpjZcYUx4WoNdBwR6RO+tJV9V5gQh6MFa3\r\nzUtVsgTAoNSCFiukq3TJK/pjrXXYd283YWlovsuUqsOXAqC+szK7h9WKcBud\r\nOPqKZYfxt2J0h47q5X1sYqzO6n4Ap+LCnICMf/jOsX+DsrfZihUuQ0v418jO\r\nVElhDZcy33fCyTjplUWUkO7McGm41ErnKHYLXZ8uOkKt/xyE+4Io2pSQaCiy\r\nnf81gTB9DrcAQpeAxrcoXP3ndvRM2n62O89onNpS+8SSOpQ72+8AnvtQjQSD\r\n6WX5XBnFJcXzd/fDebSycYM6krfskPsHw7hb4whH/1ie0eMhadamLde7DSUe\r\ntVu++VmdgEDV8+ahwKm3Mpry4WUxV/L5Lc9jgwz8pxXYTN1nxWT92k/5i6/T\r\no/qwhblp1yBNcezO8gevBjKogo3rlIKBX/tg33aN1ivf1G7xDa4vcRO2cfjh\r\nDFggGsUV95dz8ba0I93A+bexvwhuYYGWEYBLiuGYMY7qrETMoZieB9JpWoLq\r\nrtDvaA+QA06I7Nyi/yUnfig62zljWv7ngT8/rouTjrjU2GRXaKCazTdtouzE\r\n4VuAnBjD63Yy2jLjMLZpskfTiVDhmq5C1CC4vnMX72YtQMc1Ep2R20v5W2wQ\r\ngvVEtm08CzDnqUCQIiapfe8AyAuNh6F/WYk=\r\n=b5G+\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.0--canary.276.19be28fbea70121c84abc90c79eedab816960744.0_1648720506173_0.9418849598492658"},"_hasShrinkwrap":false},"4.20.0--canary.276.969ada9a6d169bcb0a1fc4d36ef206d535d569ea.0":{"name":"@sberdevices/assistant-client","version":"4.20.0--canary.276.969ada9a6d169bcb0a1fc4d36ef206d535d569ea.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"969ada9a6d169bcb0a1fc4d36ef206d535d569ea","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.0--canary.276.969ada9a6d169bcb0a1fc4d36ef206d535d569ea.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-iOQSiy2hWPoPb3910k8Xd/QqdeD8p1dymLC3CRTld4dZs7H9ggQJlmzzW2UvjiOBoi1YMhjznI9VnZs0X0Rc4w==","shasum":"1348ef8a6f1ebb086fe60985d319b034f21d6c45","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.0--canary.276.969ada9a6d169bcb0a1fc4d36ef206d535d569ea.0.tgz","fileCount":109,"unpackedSize":2089633,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHCV76R1tfo7w7hLsKDQjLUI/mai/zkkq1txrRMVrPxQAiEAsaCm4hXAReg3igh/gTY+2JtU28q6d9ieQA9WIV7ijAI="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRX9cACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmomMA/8DDzXKLfUy7i+e3IOJ1DeDzr0LNsLN5+udmQG+rqe5lDkEhWe\r\nMjoA9s5hHA3yZtwdyPeMYmS6ZOqiSpcLehqFecwnbS9EsDcdji8e2OL6ycp7\r\n9yYBl4TWvRYsjhfDJEK9yK5Q7wxApqDIwHH+qmvF0Ex0Qjpp5nfOfeS/CHaD\r\n7Gyufx5fo3hqQqxyQw1Sg1WkU+Y6PgyM9Zf1Z10nrkeDZYOErfjaf4qwQWfW\r\n8rXlkR0Q3L5XFQtuh/EpkbpW11+m1JLNcmFZr1ndsDXy0jwTd5z8Zpn6ujTw\r\nMqm3DpWbEvORVIFya/KHLJl20wQg5ubjeMr8WqC//sjdk4Xz37F5o/Eq9/bl\r\nCL5amuLbfceLka7hAvAkx8G4/YNMef11MP83PI9R6JUXBxQUXDC8DE8UDWLC\r\n7iwo9k++THaBMYhtMjrZydyb7OQhZhSXEKERKquDCVjYu8vkOpnxe4slFMbe\r\nke9UIvQFtarhLW5TAeD2BKtVn2rGtm0preyWUrA4OemzPGIg14//fq4r0SUM\r\nyBaIzeAkbr9kbg6XQMImsPFXDXKUk8xBnd1ROm6c3MtKBX+PwIGWj/uBM99b\r\nXBXWIvo9cLekWo6GkGulGdFyg7vhShZooyA7nbz6RXk3+S5GScsKK2IkYzN5\r\nX0BA2mhaSJnUiBSvEX/SdEdxg2+NqzzZsPk=\r\n=uLJv\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.0--canary.276.969ada9a6d169bcb0a1fc4d36ef206d535d569ea.0_1648721756226_0.980913409796137"},"_hasShrinkwrap":false},"4.20.0--canary.276.96dd5035304b0d12b44ad0a4168e169648018760.0":{"name":"@sberdevices/assistant-client","version":"4.20.0--canary.276.96dd5035304b0d12b44ad0a4168e169648018760.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"96dd5035304b0d12b44ad0a4168e169648018760","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.0--canary.276.96dd5035304b0d12b44ad0a4168e169648018760.0","_nodeVersion":"12.22.10","_npmVersion":"6.14.16","dist":{"integrity":"sha512-+oJjbq7XVPmc0gDBhx/ga5umcVZqv8YLqFHbei9BM6qzY0MmLb51RZzpmb60iAGKCNKKN1UOHKT5FpjFty6Q+g==","shasum":"b44303d37ac22a830a362d451a79db1f116a0f19","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.0--canary.276.96dd5035304b0d12b44ad0a4168e169648018760.0.tgz","fileCount":109,"unpackedSize":2090911,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCSjGWn2B8SGojl1RyASGyerRKToFDd/ljvIAnB/78dZAIgU4sJ5ELFElIjrGmrXoSftNM3PzaE6uB18li5P5TAtNI="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiRYY5ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrn8xAAg+edAOY5cotZJQqpZ/by8SNrtghbolbo6btyN+S+xsbrTkZJ\r\nGqrEVMfxJcwL6BhW7BPMwciusWO+IGLAwb5ADBM8+7GPl0jrdmFfh7yL7qUk\r\nZv5NKHXBClZ14StxX3WYYn01fdsAXe1cVmu8hXLYUQe1GkzheJJvi3uXbHqy\r\nkGc4hK4fh1MR7KMhy5vi85R9j3jLzSyvzxTH6HMYKicWd6ipOAymSnCkX0Vr\r\npFQfuUmzoEyTiw10rSF+IpoYus176PvZZ+8kYDnAiUY9V1L1YBpLrGEbYA8+\r\ncEH0WSzy/J8bqQOHBNKmTCPjWVruHTCLj22CmGeeNquQSJbzaPn7C8b+z8jA\r\nyOxjkqKADJKR40UeprBY6J+zv/LqdGpjcrAXjGCz4dju7ar5UxDliyc1pgm6\r\nXS156ELm4Qbz35JHWR6wD/mw7tA6yZcAkOGg8bu9rGd17PF3pk9/dNr+VSXM\r\n3BsDj8dFPmjmsNGK/3jhPSEhKKxT4ro7nRVofBrKrzKaNO8qoiSm5iQ+TeV6\r\nR+kDy3FK4lXWT3cKLtbb+CaSCKwMYqLfytWIZFaliF5LI6zTMHu6BF6b4C27\r\npZRAL8sBE6l1gz5IMRiO9c3zcVMGD4ey0jXNcWgVnH4sY+AFJcn5QOnhgqpq\r\n7YaFesJrK5SqMibe7BDG/sEd2JGpewYJKac=\r\n=tnvq\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"andrey.orlow","email":"andrey.orlow@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.0--canary.276.96dd5035304b0d12b44ad0a4168e169648018760.0_1648723512975_0.4298801805047805"},"_hasShrinkwrap":false},"4.20.0--canary.276.0b86ed74d732ebec78a690d6b83de6791a456581.0":{"name":"@sberdevices/assistant-client","version":"4.20.0--canary.276.0b86ed74d732ebec78a690d6b83de6791a456581.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0b86ed74d732ebec78a690d6b83de6791a456581","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.0--canary.276.0b86ed74d732ebec78a690d6b83de6791a456581.0","_nodeVersion":"12.22.11","_npmVersion":"6.14.16","dist":{"integrity":"sha512-sx/mMvtFTT/vR/V4M8/9JaQUYdOXXlsPh9XfnQyr+LwIrI8+Vs42FQvBbX/DiR7hMtZ+PKr4iNKTCcL2dVFFWA==","shasum":"243b63b8c1d99d907010ec7021e972494708339a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.0--canary.276.0b86ed74d732ebec78a690d6b83de6791a456581.0.tgz","fileCount":109,"unpackedSize":2090911,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAMt9z44zW1/PajeK4X1TtaYVcyl9ZPWbbVJ2myrh3pVAiB6zLNxy16Jx5qzuwqZ3m6am2X+yLvcb7pFbIBqP+X7Rw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiStXbACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrHEg/+KKhSa63SM14uI2ptDLOXVFrpcZ/DXL3jhGNVuIddi2Ad1RqA\r\ntV3jP7f7fSklGs/WaNXZQzR7Ltn4Jy/hO6L610JQKDn0L3Ulr0sLjlDt0AUm\r\njk/jzw8LumnDL2RCYfBnVNMCcSOFC2lwNkt1/5D0Qft8GHeH+z3porg3PHFj\r\nXs7XFKmeBG3iNUVEaKVrxvAGXAHlcHLMeJvGZSbnvdKzBDxIYrPt2fnGV3yj\r\n9lCJAhRMZqatEzDmvNvwJOEBkd9fMFbuhQbh5d32TeP7go9Jtw8DIFocojWt\r\njNSka087KxzayAzCKEUEUcZrPJ2aFTEaSC+00UGB5kmqtqlIu5xLR2b0sZv7\r\n2kxKJrcJysbSMPj8sfooF4XOcFmeoGYCoShDQa5+9c6asEIC7YEk8DjEI3cH\r\nI7wrRaPuDseIWRo/V/iqRPw6NQ1e2n5WSqu9H3Pof6eAHqzzwbDbXhDN+OYR\r\nXA3vTlKASl8mzTh1NP9dujOOi803lurLP2ZZm8qHdSOZEUgigNDimuSQ9pN5\r\nqHMABc1BchwvzlkIeKokdQQJMQNkIBIPV8hGQoLORim8J2XQBJP5q5wiq5a7\r\ngknGOyBoHr80KNQB1zRLkKUHlBBe7GY7/t7BPjBEnNISH2yr8N9QfkiO+n+9\r\ntaEbArCGHUtl16jYh5WKuz7kxcDeDETrrRk=\r\n=Wpsm\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.0--canary.276.0b86ed74d732ebec78a690d6b83de6791a456581.0_1649071578561_0.2830337651125512"},"_hasShrinkwrap":false},"4.20.0":{"name":"@sberdevices/assistant-client","version":"4.20.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"c9e05358499c57e620298e50e62911c35689a314","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.0","_nodeVersion":"12.22.11","_npmVersion":"6.14.16","dist":{"integrity":"sha512-QRtoZc771jRtfBdQ8r5yZqAVew2P+Ivc3Y7wT53b3XVoqELOGJg2pepG/Ogxx8P4/m0WHLn0+KYifTkUBOGUog==","shasum":"ac67d3f0893ce6d008033a7fb935c8abd4ede804","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.0.tgz","fileCount":109,"unpackedSize":2090760,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID0buxGohAMh0iAEUjsFwRUwmzL7gEyx9XbAjhydU2lxAiA+jB0Fx4QR5O6bBHwYc8CvQYEx2ICwvBwqF1aT59F8lw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiSwH7ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpD5Q/9HDVw4Wp9j4000IT1AiTftK9ipFwqLdbj8d5UBxZlbO8VmNIS\r\nWcoGc/YJcB1+Ad07OKK6NGDUP6lLqvvVUcjsTnn0remR3PGbyfqsfAMn0CIj\r\nznm6x+0fcAPFHUdxLJZN0a7ONkKajNHHQCburHgX4aFHC4SAVaOJMTURgsGt\r\nrIZF7jKSbOIUjYjz0avrUPDxMh5gjVCszjlXUHVJFnkAKEb6b4/hzej9ChpC\r\n8yyGVKYazTfhgU2n4c9BEmmglMCouYuJSOYvC56CgkE6o+MjpV7melFPckIF\r\nmwdkc1IFFxVKR4zaMFXngiyn0Wk9FVBLdCQ96PbJnawYsi5RAJgq8F7WEFIA\r\nFA4oUWlkDk46SaIlPsRMydGEY0trOk88/R/c9VclvWBm8cQ0GMIyw2l/8OIf\r\nQiltr6eueHa5KkgVaH1Bnc/OBc9pMxG9dt23teJFJQn4AlW3v7MdoZpyVonX\r\nspq8bBRiWNTT1iTR+nsOnmwa1R33hMQKjcW1GWzKyjKwCWqJ6uHN+o9jsifY\r\nL2gdBgcRY91mRoAFHbEXB/vmG9JiFj1wqMmXymSSGf3VRtP/BRdKjDJomGF3\r\nbcEgFh9zvp64ZzUvGme0A+xEkYOqFg9S5BCJtCcpk8gaxyZesXhvJBiS5tZ4\r\nXmfNDC0TD3JC3SDSt60IfArRZ3hVuzeRAOg=\r\n=hrA4\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.0_1649082875650_0.2067302864004965"},"_hasShrinkwrap":false},"4.20.1--canary.277.b1f79a79bc166424331fca6842e09020ff254f64.0":{"name":"@sberdevices/assistant-client","version":"4.20.1--canary.277.b1f79a79bc166424331fca6842e09020ff254f64.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b1f79a79bc166424331fca6842e09020ff254f64","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.1--canary.277.b1f79a79bc166424331fca6842e09020ff254f64.0","_nodeVersion":"12.22.11","_npmVersion":"6.14.16","dist":{"integrity":"sha512-FIAYORlpwk4SKRLuGYEgHlonsrBKfmcZmswKX0cYxPqOCimhvsa6D8P3F/nPRprJ+4hVI1c9yz06a/MGqfzkBg==","shasum":"c3a8a033ae1705b26fd5623f81204f13eca43640","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.1--canary.277.b1f79a79bc166424331fca6842e09020ff254f64.0.tgz","fileCount":109,"unpackedSize":2092152,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDzqpyqx86r4AJ5EYcL5QyFIT9i+iE7tDIsFIFVQepuCwIgUAnzJVv1ONpGGmgl6R7dwy25FCtc4nalQqxvdkqjHhU="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiTC7IACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpFaRAAno6q+Ch+HPXsIjiEf9Xs3hw6X3N5WzSqnN3KcbT4SFaNNer8\r\naycjV4A/IhlWohbXyYz37defaSMWOU1oBsck45skCiCnq+7/6quwSZAUVTgb\r\nIpUFKITXmMbfB0jEMokPNLcVKWXpP4JvsXdMcTjbOL9noqveHNZrTExPASZJ\r\nz9DMa4nHd3Cv88BP8tBvKZbHOTUISmLf+x5pMZWf3V0sN2Md/zrHadaSJIa/\r\noDJyvubqpss2FT6C9M9V6nwkzBLpYfXHV5RN3ZS2tOQTAMy1yCUdNdRWo3BJ\r\nFli2f20+MSqkT3khGrJ57IWYROi2QA3lrPt9R0Dp0ZqilwvIhvuugifpHM+r\r\nyl5EI+HQ/u7N/t4dCPKecwsyPy+4m5ci/KzsLqcBtjxbtJ8n4ebFqQtf3j/u\r\nAnIKhwslu83CGQ9qzk+o9so9DeutJ9Eyx80V776mM6zisn3hjtTXlXaspVRl\r\n59PyYVCC6DxRNryITmqUx1xCXY0aEaTHQ0MFShkXW/64c+Dmf0CxAecNakcz\r\n1OOpPn+zzAj8ds96J2YlcTJ6jegXbSNhHVthpS2KCKjwVfmr2jyvd5pmUtjn\r\nbS5NlnzX7JAAY+Ya0yWqnmn4ZXMvbm2LlZZs3ScMsFDaiKi/iecszCo9jqZr\r\nqVjsWcp77VZtUtPo+laL1akuDozRaycP+DM=\r\n=w2gk\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.1--canary.277.b1f79a79bc166424331fca6842e09020ff254f64.0_1649159880738_0.5503333300460409"},"_hasShrinkwrap":false},"4.20.1--canary.278.a1fb3d0e1c952c648956184c41226fadee9bf732.0":{"name":"@sberdevices/assistant-client","version":"4.20.1--canary.278.a1fb3d0e1c952c648956184c41226fadee9bf732.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a1fb3d0e1c952c648956184c41226fadee9bf732","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.1--canary.278.a1fb3d0e1c952c648956184c41226fadee9bf732.0","_nodeVersion":"12.22.11","_npmVersion":"6.14.16","dist":{"integrity":"sha512-wMd1b4xQE4ymXyZrLjdAIOyqPd3bbokus+njOELoCrCYonnSamdG/heoHWKbyrjcMc5Qlpdu1QVVBI6zsID2kQ==","shasum":"8b60ca5d739b0493bba2c20e65d3d3f505f60017","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.1--canary.278.a1fb3d0e1c952c648956184c41226fadee9bf732.0.tgz","fileCount":109,"unpackedSize":2091396,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHuTiTW3OgaZqyytkj3/QZj9QDxCpCG4dvFqbn/YQ/e3AiEA6y0bXUva5clFwmPDCuYqmE8eGIlJq8lIegGTONh6dGE="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiTproACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzjQ//WxyrCrPM2SMSi3DdeODewt7tingzg+Kn76bf/4phq5SmjouT\r\nwGH+aPhUrLedULmmXfVYjVM35Go5cZnS4ufNjjrrwgMrDWRlcpuqJrMqOc2J\r\nfEG7/ZA1XA2FelusNy4Y0X6ARF63xZduQAXnkhjD5IBo9111aGQmibJ5nNH8\r\nV35hVjBJPek5XaeWar76G6JlB0E8LkbBZ2sCmHCMe3YxdGP0tMDYE7xtN9rO\r\n5ifNSc+EEYugPRhmy891lrQCpyg9gLbaqBifetv1CldgVjs53Z2mLHuecuzC\r\nTES9dwt8DQhAwpuvRJL5rVKmP3cjFux2CNu8ffMvhPFC0PoPlWiufma51W5K\r\nLuvV2tD4m/P3ox9ZiOIFCsLOemTRgoCEAqYmJ5pJX5PhfsYRiHBjbjifAruD\r\n6Q5lK5OeFfrxZkp618SBNoAJvshEkHa0KZnzaaLV8ecYHipA8/0icY90LbE+\r\nN3GsekjKUsHTlbM/QLaTN+cS/tnQyDSNi5D1nOQpH+rPWFo/kUWWkmKpP4/j\r\nLKqgWTBT7TGDtb8JPEfQMcs9RDyGvKDo3OAkOKtVvuziCk8ds0KWTYl81/59\r\ncLVgPQmd+aRljT5QtWz+twoJKgMvwJJs7WGeGBBhiOuxVTpr4ZweaggnU/wI\r\n3o+j9wjujmDzatfSBF70THNbKfwHWjNWTCI=\r\n=UKsA\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.1--canary.278.a1fb3d0e1c952c648956184c41226fadee9bf732.0_1649318632301_0.22442498115941012"},"_hasShrinkwrap":false},"4.20.1--canary.278.6b5b00cbe544f75d5f7c5c5637d15f05c5427f97.0":{"name":"@sberdevices/assistant-client","version":"4.20.1--canary.278.6b5b00cbe544f75d5f7c5c5637d15f05c5427f97.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6b5b00cbe544f75d5f7c5c5637d15f05c5427f97","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.1--canary.278.6b5b00cbe544f75d5f7c5c5637d15f05c5427f97.0","_nodeVersion":"12.22.11","_npmVersion":"6.14.16","dist":{"integrity":"sha512-iuHQUDzQgegkgVI1JuVNPoQhuuGfB0Qx4zQcOQXAJXwMqS/CFdcUjSmLqf5N6b4fwtRgHUYwZucgflRr+0+Upg==","shasum":"878391512e413341c1b5f689b9c33583386e5e72","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.1--canary.278.6b5b00cbe544f75d5f7c5c5637d15f05c5427f97.0.tgz","fileCount":109,"unpackedSize":2091614,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFSJaJFwnOZ464t/dH05N2iHnbCNONQiPkRnKUrGRxUdAiAbixdlv8LgvNAioTLOWqU0osWLkmV7xUeZiyAizXdwuQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiTp27ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrhEg/9Hex3LORHay02CovplF8w3xmp2VEgQaBbe5E+65VVo6ywNFXw\r\nBLKY7uLrv3Ck59MehF71TbO9A7MCso1RSvRMOgtTxL3273VMQi3j4fGOtSJt\r\nqxzOjeiDHpPyCCFJDBrc3GcqUSTBFCswZ/vWi0OeGV9rDUWS4nLvxzFNmukF\r\nLZqFkF7wWjxbF+EMrRitzKtoX9HVaU+gRH1SplVdmMHZV7w2LqzCwxyRtOPo\r\nDUEfYt2bGPcHcoBqls9fJY3Jao6rg+7D2isaq1ESW2VBp0UH0EPjvY3HMgQx\r\nRjl41GLyUIL/kQyswsbAmc5J/Ru1WMcE8maWW8WiP4YvXPjBNAO/5VupcffK\r\nx5WthjsdJp3VEkx1Y8ZwZce8u7RNViHWlyR2aIOZsMmmrkUWQ5dic1g/983A\r\nkniWjH/h3ymeAlNh2EKQBmHclY0VLSj3NMHUMA8EF+zyTZASuhq8URk2V5eS\r\n4WRuGge52mXHP43T8yIeEogwH+d9kfz0H+tpMz/+bB+yTvDF5C6L233ZV2xA\r\nreeU+bcGs9OHclQBf0L2uap1A/TIuQMbHDMRvXBoFYFigA4rmZkPoQFUrqLx\r\nlTldZDOeBeH0PHpCKQmk7aC2QxYuUp/XKZTUzIT7OgQ34HLuSuhg4ev1ZU8l\r\nLT5LQZalZGdW3/fOk+E17IWrHMGZlVRgyxw=\r\n=RSfp\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.1--canary.278.6b5b00cbe544f75d5f7c5c5637d15f05c5427f97.0_1649319355375_0.26409786707414007"},"_hasShrinkwrap":false},"4.20.1--canary.1.42882348281daff5003541ffdb7ebf0000144ae8.0":{"name":"@sberdevices/assistant-client","version":"4.20.1--canary.1.42882348281daff5003541ffdb7ebf0000144ae8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"42882348281daff5003541ffdb7ebf0000144ae8","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.1--canary.1.42882348281daff5003541ffdb7ebf0000144ae8.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-hQWsnKyYoKNVAn3kcJEg1j6Dr1+TfG8SVb00DQY6bLK45nfof+3Bwj5o4gVF3HNGVLMTTVCDfe6146gyle0JhQ==","shasum":"2d0910f509d34f8f4d4ade6a92acc380554ee96a","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.1--canary.1.42882348281daff5003541ffdb7ebf0000144ae8.0.tgz","fileCount":109,"unpackedSize":2091288,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCzmud9Jx58a1QldPi2ZmdogTRTdgQPAwDfX7RGY0Y13gIgRrTZ0wunI7SSHZhFpNlYZe8R4c71YxIiRCJa1dDJqg0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiVvJ3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrHdxAAkpVZlEG6/pGrL39hes9FTRAi1WrBORfApRxeQ+09wnDG7NQN\r\nDfz7jPrF+zqHKfUkbRpeKv4/lNiQn8fOW23DQtFieiVkGNntE6WLblZoGXJ1\r\nfzdi3xJ3xnDtuQeLq0vCtb3754Es3nC+GP1s13se0OTdPEoo3q5xnlhNmCUP\r\n06sDlzn91KqFcGsgIWTpFtzRhHMEco0tZNB2j9cL99ab/E0EWL4Seyzr3nd7\r\nZX6vkYmN7HpCcu8U05KeVjEGdJWfOB31+xGOkw89FQQXrfFWwfKnfGcSXSmR\r\neQ34gOVxhAR6LmoJzHvEXwTpg0acSPZLaCY2eKGHXMTQIhl5AZ8zwlXphvFX\r\nO2qNhI/mfjjOtBVSbTv5qA2ExL6ialG49nZLNw9Eudok++lKR8ajUTr2+vHt\r\nWRm48rn6mUv8IENH3hmV4WDtm/9RhfUuDu20s90kocNrHnMWQ8GfMs6PuFhR\r\n00Wrbkjj1FcvAWvawvV7l7iu07wup3qoDi6mQmaGoCZGRt9h2U59KQSGnsbD\r\nUwJLRbr43TkIoHe8agCwIoPNmGcGax5ZKbFlYkPJIDZowtqAqWCyhTE8xxs2\r\ndbmrBpmfi8LwBB8PyeAZLyerCIOXq42BBHH3J2B1p3bxi5jP/CZ+OgNOvzwN\r\nft31F21X0/FvG4Psnh5y45p9fPvNcLLYcZk=\r\n=4tUX\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.1--canary.1.42882348281daff5003541ffdb7ebf0000144ae8.0_1649865334872_0.30650313271341467"},"_hasShrinkwrap":false},"4.20.1--canary.3.e1f3d78acef2870ad6f651f121a991d547f838b0.0":{"name":"@sberdevices/assistant-client","version":"4.20.1--canary.3.e1f3d78acef2870ad6f651f121a991d547f838b0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e1f3d78acef2870ad6f651f121a991d547f838b0","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.1--canary.3.e1f3d78acef2870ad6f651f121a991d547f838b0.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-NOvexxVBKLgvz+1kix2rdWJj9KssAJ84HvBaH0oLfadySdN3ZECWXDbkfggtHQvKs56VZUa5cj6SOYybnf/ZCA==","shasum":"ff2d97e741128e0f05a10296159e36185f1a55de","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.1--canary.3.e1f3d78acef2870ad6f651f121a991d547f838b0.0.tgz","fileCount":109,"unpackedSize":2091250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFlkZvai/2Od8/EyCchK+W24XJfm654Z73xdTF1ifX+LAiEAhKx97Y8eg2+GFR6qe2bUt5TJ7MS/iXHGBkZjOZ9AVwY="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWB5iACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoTag//eQOU55leZ9vE3k0QWNaYB7QHt6OAvHQrm9CTE8Cj+dVe3rER\r\nVOp/Abi7nm5D+zwxbaXpseEr3m8aCrWcTtTXgyObljn1I0q2ksYaGtgCfGh4\r\nesNJVcUOztDx11TksgC4AvGk3X5CtCWC4YRSxsEv9d89zK6iN44cdjt13g1X\r\nbf60Xl4RLGDOMDtsId7hVPDZp5uSi8D4dQn6RTJ1prx4ZlFiQB3YwQoj+EhN\r\nW+Sk4pPRxR2QlQK0OxRlvPmUNsEkbVev9LQ8ndL8zeEWhF/Y6NRSu4S0e4Ch\r\nUf5CSKuOAh8XEytvuu7Vz+40WJH+9rLFxYTb/4oYvRcawxvjSXxVdAdo5pVJ\r\n/6CR5f37Y0Y4ypgFU8msLycIXzzJwZ3JE6pQsrOlgndiN2r9ldE8T3ddEqDx\r\nj2/IcdtuDAfuAOmcAJeg7an/b5G1n0urRTgS4LxUDbirIt1cLbp7Kj2vhouj\r\njuAOMA3NQfTqx4pxLwxYpIquI119Nl+uAgiz+yxE3TdcZFeu8B0Hua0a5BS1\r\nafQjbekCA69TavU8JyvIVB7n3m8BodJEebKv7MaE3XFWw6XU+Uj8EQ1ObPv1\r\npXgGSkl8haXITlr7FGdPF9fLGxFJ44SKYYA3MMkFknG8CZC5qPl3lfYaZxVJ\r\nFy4rCLChm3Csxu6nEH+llB6eWRGODkNpwok=\r\n=HLg5\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.1--canary.3.e1f3d78acef2870ad6f651f121a991d547f838b0.0_1649942114282_0.8262465138529274"},"_hasShrinkwrap":false},"4.20.1--canary.4.ec91ce618c1f4d54f66069a3221e17f9cc6113ab.0":{"name":"@sberdevices/assistant-client","version":"4.20.1--canary.4.ec91ce618c1f4d54f66069a3221e17f9cc6113ab.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"ec91ce618c1f4d54f66069a3221e17f9cc6113ab","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.1--canary.4.ec91ce618c1f4d54f66069a3221e17f9cc6113ab.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-vPCrMseFt3G1i0h2Ti5VR+X/G85WZIL6MPxc1pPHLdoCksjXAYbLga8sX/H3ObLOtogBT6iWXu9VeFVocb8yIQ==","shasum":"c765b2bbd350d10a75adb1ca9efe73251a025946","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.1--canary.4.ec91ce618c1f4d54f66069a3221e17f9cc6113ab.0.tgz","fileCount":109,"unpackedSize":2091250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3rWJR16TlPXDGE+jQ1V2XiX1dhUeEerrHUOdJQnsNmwIgGnxVz6KD+PzT85RR7IPQzDZellIcoGJM7J7vw+dEVsM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWDA8ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpocw//T7DJLmmCDohd4R6pd4R1876BIt617UigXXeeYtKOJFWZ0l2s\r\nJZGMZ+ZtaYuKOgNWHI7pTmmhEtvopkF/C791GR3tVsTrjMc0z+fo1GpKTo0/\r\n8BKvIk0XQvEx/QqttSOiQc+Iy62IdN/pbao/+Mwn/JmCijKDGEnvUFu9q16w\r\n/Nyq84aFw01n7UGLPLhVji88MfyWjst41Kn09IrWqAZhfoC1Of068jtcT91E\r\n+CC4lWOy4gYD4nuhCpPRGdwdI1x5vFlg00vi8gu5w2R6pVYQiMxQWko89fLh\r\n71YHwd3xG4OFjSynxqRMRxEpl+GF+/Yg9SS4LIzIrDKbrqLlwb/Zv+GGvY0z\r\nroIlSkmoVqqtoymORxKXwoV39duuXGVb5uuMXs72QnOnz9kPVlo81tJQhaUB\r\nZK0VzaEwcu29FDuGTd/f4RNgMHsHFsG0vNWkXyIkRhj7hjmIYk6FyxxJhaqX\r\nCWHG4KU2KDAPaau4XFeKts4Kugq9DSWJflQfkUOaEA6XUetLMEJw2M71FJk3\r\nwyiYcRJHLYb2DGuRpczcnbtdZnP62Jm+eHnQ1s/JP32376MEcGrWWvuoEeZe\r\nj1i8zg1y0MuQBzvCpeG5SrQtperyMvXeKf8Wf4UPtOJ0A59o+zjbYI7eNjSg\r\nMfMMWlAsf/YNIMPCF5KZdYy/2YdTINPHk9U=\r\n=qg8o\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.1--canary.4.ec91ce618c1f4d54f66069a3221e17f9cc6113ab.0_1649946684213_0.574877559867663"},"_hasShrinkwrap":false},"4.20.1--canary.4.be8f250fe5a2e56e398b1bd85b407b0b61311b08.0":{"name":"@sberdevices/assistant-client","version":"4.20.1--canary.4.be8f250fe5a2e56e398b1bd85b407b0b61311b08.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"be8f250fe5a2e56e398b1bd85b407b0b61311b08","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.1--canary.4.be8f250fe5a2e56e398b1bd85b407b0b61311b08.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-zLkWY3tfNXHameSxYGsJojBLoncEYc6HdTZcp5bggFH8Uxg9F1MWE6pHLE1tU/4BzQ0YNuqYsIdg41tAw4hLOg==","shasum":"43c17e926b9ec221ff3c5c0f8caf32b188542e65","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.1--canary.4.be8f250fe5a2e56e398b1bd85b407b0b61311b08.0.tgz","fileCount":109,"unpackedSize":2091250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHKZuovz1YAMny5BDUBXECsYnsbN4AfNuUyCeoI4Sil/AiEA2MRdORJlJYIegUGtDGL7LRUK9sAUJCH6Fy+pHiU+BNI="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWDNSACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqrMw/9Ef96ijqg/7bRdCU98A1W2fIh09hAnZB21IPgaY39Xjp/GRBF\r\nYdU99qMZUD0WH7a6TJT9k8KoQDTWb/PHIqJtmA9kqTPZTwnh8V1vBU5TpBve\r\nr4iYxS+rj/k1d3iaVXKvYy7nv6u9vUaRUxv1N3pTQIWWQYnRX3GGkFmshK7m\r\nJ17HfjCYJyp1Ft3uy1dthNXWOMa6/qCebW9A7BPgzPSDAnn55YwMyd5rdwGn\r\nVY4TRoj6G6HvKgOxQkHKtKd6167JUwFA/dOaUP52f6+iijzyQXgwEPtUMrMB\r\naAXz3F3kORCMTD5ha9TtYi91ptPBvuO5E2nU6CcbvkJJrc9eBpPSQzWrshCy\r\noDHqmqTO9Q1QA+AMD7FiRCv4nM/BfBdI4q0OvCsj+CwcIZFgLbDNIyfaFfd4\r\nfmkZiQcnf9mRwdeq84LGs2lQ4DMXhBnqSwoxovjMdTDwyX93ZhKmOiih+EEg\r\ngglZIXIgGOEX3CoQWox7sEisnJAFrrX5VT/+6JB2vXClzfqzYonXKeEqJqBg\r\nEB0GoDfW8ZK5QQyyQYZ0Hr3vAl6g1BxxBihFL2u3hPWVaRpJ8FPLkcfRpmnY\r\njmnSmZ9mA9SDkraKXhE14tJ8x121KZNZaK9Jlm/IYN9jubFVd3Jom18+Vt8f\r\nNTpkc/854PyxSl51VYF669vZNteaYuGJvwQ=\r\n=QU5U\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.1--canary.4.be8f250fe5a2e56e398b1bd85b407b0b61311b08.0_1649947474085_0.5307109459988679"},"_hasShrinkwrap":false},"4.20.1--canary.5.919edae5629fb16ac539a1c9a44247792c813209.0":{"name":"@sberdevices/assistant-client","version":"4.20.1--canary.5.919edae5629fb16ac539a1c9a44247792c813209.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"919edae5629fb16ac539a1c9a44247792c813209","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@4.20.1--canary.5.919edae5629fb16ac539a1c9a44247792c813209.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-UVVPhr57HAQCkTpjdn6b6VWALCM4uNkdcL/o/Wpn1fjhdVrdyvlsZ0bTMME0o3ApXSSQPlnhZW+WIkkk4Y5/rA==","shasum":"a275771946eefd0739bf3f1e24ff4d518c69b4b0","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-4.20.1--canary.5.919edae5629fb16ac539a1c9a44247792c813209.0.tgz","fileCount":109,"unpackedSize":2091250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICoTsW+xsC/N4Z9bha7PJxHc6Canv7B64vHzuOzCaeAvAiEA0+H1hmbAgzX3s+ctsm//LKbwT1p7Cc+5JOeBW2x7N7U="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWD+mACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpLbhAAjrsRf5wBkHwYbWNm6eMOh2iLniTDVn4820dW4qu+mkNb1Ogc\r\ngQuD0sRI1B22u6Bn0/eJLbkqTJPn6RMNRTKXG0NCnkufckkpW1Em3ODRKRMz\r\nIAaD7hR2cf6mZyIOVHDYdrE7ELVUpPhon/zwoMxwZhAf4RUed09xTaAxCISr\r\nsCzkdhPgmGIySb3MbdO+IP9sNIOMVtHJ9By4QcOSJPXvhNeIhX+4AbgBb1Di\r\njMN696b9f2uifrfUFWKv0m48oQF4Ow3gtMF9TLW6FLtgF0gDLLJBHsv/yjK/\r\nPq8B3UQcheEdj13s1kbDhpE2gRYTVlKsEak5QLGuDcYjVeyVEjfZZpiceJa3\r\n+rtYwTg0q+syUbJpCoaGwoHJ34Ln8+j3Dv20cGU/NLx+Cs7SQD2RCWvceScf\r\ngCCXBI4bb4YdQhpMnqNyYd3DxRUWRgiK5yFZj/Fvi3QSmVw7zaOOCqK+EsWV\r\nHzz9CjFBeWQmBFOr+67ovBUS83xdIaZXmX81A4wkEkiY5lprc4QvMW9HDw1I\r\nninhTta0IgvzCK6+dSPrlNEjvlPrFQaCUEUURPhEjl3uYrKNaRFWLAZxR6wS\r\nLfW/gFVNn+HNwMeX7SkWZ8FY+uO1nhWOPeuGcSAmf5FNbZSvGlZ0trJs3Mrp\r\npQui3dU2IQmZ1p4yMTEd9PLilJZ4c0I11Z8=\r\n=rBD/\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_4.20.1--canary.5.919edae5629fb16ac539a1c9a44247792c813209.0_1649950630045_0.5300937290153722"},"_hasShrinkwrap":false},"5.0.0":{"name":"@sberdevices/assistant-client","version":"5.0.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"be2ed09b2420e578e83765a644b3badb171d05e8","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.0.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-VSoLGyXUYc7CWTmbbYRQVsktm6n097FV5D4lAn5W9YQ6bdyR7JZTRHs+nxli6Iulv97wx2n3WhVyE5k1vt9gaQ==","shasum":"23c9d4e50206531bad65ff1a3b783ca3bb851803","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.0.0.tgz","fileCount":109,"unpackedSize":2117182,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEnrZqlxHD+hOUBWx7O5o5MjCRWXpZGhOIAGK/mE8slEAiBo57XeBtln5nLhlvl/GweVxa9hN5b3w/Sok82MQnodgw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWEEeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpNBxAAl5kjYY1FD55zH/VOEwnIbGPs5fFdpWabL6QElZcExbnnz+kK\r\nhJ3NZ3XW/YtmDGzG7cF1Iwnetu64A+N/uFiR7dN5I4W8YGn20LDdXC1rjvIX\r\nD5GRjmc8cl5T3w6XJYqWOHstL35TAbNrPAJiahHV7FIz0NcHVdApdGC4RvvS\r\nL70l+mBvoahJiWCoXy4NiixhQ5bOH9WjLfyaq9qzf/utN+K56xBs1G29uUqZ\r\npPdf/dZRvUbyYEI8hCFq5xqIlseZmDse2GN+mJx9qmmX8BIZUySedsDvA7pK\r\naLvrjSXpwVjv17Uqg0dDekDlG9cek4hEFjp85Rn/CUVwKVVPVihK7a9DTCE5\r\n3fn+55CZsDA9f7SB3tHPnAvRNoPZZjs82JM6cEBS9sHoE8UG/deEqUxWlLKO\r\nUjzHWCsUwC0JgD4Td/JRiPIe+t1g5kLLzEI5G6Q+nsXwiZ3Ac6XoqQWabknS\r\niTvVOVsLmu0FInI1XRojCpIDK2jmxQNW5/iAx6n2NQiqvPJIR+QeB7vBnR+D\r\ni/d24I5fxlz/GxvnBtsXr++wTyEGfbWRaW8vm4k4gCsZ0XRkxRjkII07OX6X\r\n30BvwMy3lspnecM4+L2EI1OjsG4EMmJ2Y/8ZajWHmFOHhjth81H4qGEzavi7\r\nvx4Ron5Q8lCv0RRuuOowpGKh3yVmtkSQ5wg=\r\n=OhkQ\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.0.0_1649951006300_0.906150710330013"},"_hasShrinkwrap":false},"5.0.1--canary.6.23fdaf42f2e4bcb9798e566a9b2dc9eb5f0b3a1d.0":{"name":"@sberdevices/assistant-client","version":"5.0.1--canary.6.23fdaf42f2e4bcb9798e566a9b2dc9eb5f0b3a1d.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"23fdaf42f2e4bcb9798e566a9b2dc9eb5f0b3a1d","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.0.1--canary.6.23fdaf42f2e4bcb9798e566a9b2dc9eb5f0b3a1d.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-k0nxHJ4aoDdcMLFLaH6feRVIVUd7v70K0+rYhkGdzljVY8Hb+ZBVZdsMoSuhiry0cNlCvtsGViyydv2nY8MDxQ==","shasum":"72f56f3059fb4fdef1cb84d23c02b004ea5f0d84","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.0.1--canary.6.23fdaf42f2e4bcb9798e566a9b2dc9eb5f0b3a1d.0.tgz","fileCount":109,"unpackedSize":2120283,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC40dW4hsSac+PrqJToGv02Gjhc8Br5cNk6Cjphd4t6cQIhAIsQ/O2nUTDXNKsJ3qbkEQo3bSOJormY1s0HSfeU43iy"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXTeNACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo/hw/8CUKPT4csja86QUUKDMz8KlwQP3TgtDV5PzfUB1SEByev31hb\r\nVgdFbIxp2d1X3tO/Xuy5V1d1lkwezHL/fJ0fLtz6z0236nVouGOLJTQkBUn4\r\n4IEnQcvDhzi/b/bsj8nGnXEb09LQO/pEGFOC+hG3fHTS/xbq6gGj+ELPPt9N\r\nUjO3/00s55GOxZuEcPDxxOnFqZtAvCbXrDNGNIaHeuirwcYrZyuJ5wduFTSD\r\nDOgTdwiMh67bZ8UiG5f/suoAq+CJI5xaRXqK28QilUCOVHxrGLYnlyVE0kpi\r\npKsRB0r//oMEv00dCjvV4P9y0xFAyvyz8rQ6x1EY9kujhIMosfgM592o7c0m\r\n5Q/unl4q/6c43S5BcOk6VpwhlOWU8rNPSCD7aYfUgGlgxIpbfop5fOhR3tFU\r\n+g1I4/XLpD+2sTYnqfqwuN72vswegkYeU3atocs4DbIRyQTfnSioA4Ma9rYe\r\noTZjl9W/5i3pltJ0FHAnzv3YUuswTjlq9/n4daFW0XPeeDg0d6ru7/Sn+kSS\r\nTZFE7PLS2NnedXM6KM7SAM4wGUpD/3Q/ygAifq88TgOaLVXiJ04rcwzaNXCp\r\ntqNbAwk62qEV1osR65aaPq9396JIQWlAmsyxwX9OifgFwzuIU98E0Y7qYvDe\r\nolgMbSapTiAavZz8S+752aKi4E2a0cc0+JA=\r\n=2pJu\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.0.1--canary.6.23fdaf42f2e4bcb9798e566a9b2dc9eb5f0b3a1d.0_1650276237295_0.5020775617055897"},"_hasShrinkwrap":false},"5.1.0--canary.7.e3ce281b5547f418b4fcc6bf10049972ea8bf9b8.0":{"name":"@sberdevices/assistant-client","version":"5.1.0--canary.7.e3ce281b5547f418b4fcc6bf10049972ea8bf9b8.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e3ce281b5547f418b4fcc6bf10049972ea8bf9b8","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.1.0--canary.7.e3ce281b5547f418b4fcc6bf10049972ea8bf9b8.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-lbRw4hHIKuJ8tOX5MVCdZwl/Aw92o6HUA+RTi4h3CVKDSLC1sJyHN6S7OLdGrnByBPHxZz4Oli2DMamu0KmNNA==","shasum":"ea53005541a545f25a7bec837fc5a8f17a507ee9","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.1.0--canary.7.e3ce281b5547f418b4fcc6bf10049972ea8bf9b8.0.tgz","fileCount":109,"unpackedSize":2117499,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCD1P/DqW1MFZLBkcM602OiGu0TT2eUJmG+F8+xcfNvQQIgbfQw4B2aewAmleHAHuqi83Uw8afvymS8PtyoCrPkbUw="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXUTeACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmol/g/9EpJMjSEhn3U2pSZn5odIWePtJJd61qF0SP2ivQx0vE9J2f0S\r\nTyfDCg/HwAM1AAK+6OCXd1mstrG2fl8Jt/5v6j/QTzbtHB5r2dFVTYshauo5\r\nG3Ex/l5+kwjypA9VDu7e7I7SfEZQe6TP3qhFxRzS86QviOQdLWD6seWUTlcK\r\nd2+Er88qQBBmNhOdrxqUWbAQupEmfd8J63w3BoTjouJhgVCBSzI6Yy5VrEZJ\r\nhNhZAdE1u/SzJGnlSxjvyysjM0RyPCOEhKq6luPORMUG4pv6txmvCMuWOdJ4\r\nzD7EVaFAvwTug5xsZB3ovwWtItPlfUNSFl/mcrDmZ6posO65t8P27gJpUIQU\r\nkcEIESCEdN2YNc+rD2bW8ObN8UcrE9FjjBeCPmcortfhQ6AruxFnUcI9mey4\r\n8012UKLMZVs3bJ2KKA03DS4RVdg1evnQVWxXXH1i4eCliWYj9+RY/Ma2Jyo/\r\n3q9hBf6l2jFr7pw662lPXjErJ2WMrzqnmGRX4uwqTAOXDdAZRrGu6KLtty6U\r\nwKQTp0Jijjjr/Ak+fAyVSJdynDM7jxuQ/NB/ITMbYcDauPvKrqWr6wMEDvNs\r\nLsuRQ9Lj2wL3E2gPHvPvMYfDAYtkNg2Jf6p4NWieUzPTOwAYH5QnhsxXjl4b\r\n15VdrYvPM7xNaVJAf+nvUECQjhfqUomkrHU=\r\n=UXVP\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.1.0--canary.7.e3ce281b5547f418b4fcc6bf10049972ea8bf9b8.0_1650279646305_0.7836857199706504"},"_hasShrinkwrap":false},"5.0.1--canary.6.b65af659d59a65342fb88647b2baf8545954e7f2.0":{"name":"@sberdevices/assistant-client","version":"5.0.1--canary.6.b65af659d59a65342fb88647b2baf8545954e7f2.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b65af659d59a65342fb88647b2baf8545954e7f2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.0.1--canary.6.b65af659d59a65342fb88647b2baf8545954e7f2.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-pYd48sfNHsoE04M+v/aqXY74SWShoMqk4Sb3/JDN5i5pA7MWpCNWmDYrzoRy4NE/pjiTlpAdMbj7TuIm7C97HQ==","shasum":"a640a1c3368b1a834a1454e3caa0cdc6b0e2a8ce","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.0.1--canary.6.b65af659d59a65342fb88647b2baf8545954e7f2.0.tgz","fileCount":109,"unpackedSize":2120831,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBCaBGOxPclM7WrluwlvmHX6obCRGm5M/tXzt8zkkGXgAiAxfbFLPLz2tm/6v+8R+g7xzpc6zIVmIopsP/S0+LFa/w=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXVhUACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrt3w//cubgBnJ3AT2j/PfaLhhysCEryKbDGpM04IDUC9Rs6IIHrp7d\r\na/DhslrsK+uOd0SmmrYxf4On4gtl16xpS+OD8lW+5Hxjecwtl4Be+2/22E3z\r\n/aonsVctZwWTHOZXuKM+vYTvskohoA5VDZ8EcekH2Ob5DU2H6KsD4tAAte0G\r\nT+BIonAUBOgNQTlAVfTgjO4/5nxL7z6Ak6XTgNgw6yVcSC9OJ/cB2sCNGy+v\r\nDDT4aLxzK2xTi7XBTTuUs1w6uBWfwukpbP6qFji1AA7bASuNLfOPPinWcGT1\r\nxx3pzvNneN2C/CcW6W4RroAz1e5Ua4AaDYOu+cmKseY9AAsgJAUVLKL3o9hy\r\nOzwzuw6H1ZgFnRX/buL5dMz5YSj8CNkFChRy/wZJBoD9EZQQmZzrl0fgpUxJ\r\ne1/35K+l8cfJfLr7HaYw4y1yagKyWAlJaq3TG5UiOAtcKbxDl1V0bHd/KeMZ\r\nICxPectwhnBYaPvp60R6mBo+xEnvJt0yfU4UxVIgBOFZUULijQmZU25ctXXi\r\nAt1PNua4qpGjdbSnRXjkO7wLMzhgUVgHkqiecuW1utIaSGQH2XRsSIrO91qV\r\npjrCYWD5ifyv+erJpXUi0Sr7y2J01jrlEjgHUm3BFTwId+zuoxFCV8InMh+d\r\n3E9uL/X4pn7Ta2GoEffine86bzqagFYoFEg=\r\n=5beX\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.0.1--canary.6.b65af659d59a65342fb88647b2baf8545954e7f2.0_1650284628068_0.9591941008420033"},"_hasShrinkwrap":false},"5.0.1--canary.8.d353094d51b9f17f2357a4dabb88993db09c2134.0":{"name":"@sberdevices/assistant-client","version":"5.0.1--canary.8.d353094d51b9f17f2357a4dabb88993db09c2134.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"d353094d51b9f17f2357a4dabb88993db09c2134","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.0.1--canary.8.d353094d51b9f17f2357a4dabb88993db09c2134.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-jfv+qTKeRI5l2/eOpsj/ZuGmXKZaTG+8ljxeNGb7OU4UHNRhRtWustHDSPsyxegJsky8lnDGV1GIMn5PQ0YREw==","shasum":"0bbd51cbdbc899b84a3046823cbaa266ce2dfa96","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.0.1--canary.8.d353094d51b9f17f2357a4dabb88993db09c2134.0.tgz","fileCount":109,"unpackedSize":2118916,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQClCHBkzWOJexVoxeCt+8rsOKqGE8oBhMAyNHa2tk+CYwIhALTyXomx0szViqXhQ4RMgtKQg5rU0uni2tIWnBvqqMGR"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXWpDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpg4g/+JnwLO+zPOAXzibJqG74I8hkeKw5zI0a9ReeYA5/jZmzUxtrI\r\nCvVo/H4o3eaEKfwHQGzoANricII5EPjjELWe5j49v/CkbnPmGNwFcMZ1OdSJ\r\nQ9m0JNeDi6okdFBQMunmTYv1mKAVNKH00BDbfYE5kXyQDiOZFErQx8Llydaq\r\nszqSG4spE9QpyEZemJRNIAIEwPBGP7qrzoTxvUWlfZd/z26nOHk8RxwzdS9T\r\nL89Jo4/t0umQEqMYWDzpXujwbaxuDOa4w7C374Ogql054uTCTh6RAmF2vvLi\r\nApPUG9jAjuPgmtq2/mEJw2V/4OtnE1QwlL6Dddtf4Y5DG5RrfNUPyFOohToZ\r\n7h6HmNql4iFD45OAEs7688vRqAjY/ohbh5ym8sLmM5UO1pHj6AqpWmF5O+AF\r\n9UiogYHNbGALkxHSWxd+6qXGxCY0keoi1wFaoctgMq5DmR54oxc3O1V+z5pi\r\naMwHcerKQve3j9cHdyyFHfsD2SseAEesIv44/77gVzbrOkHAzfLVKsRtxqPz\r\nf81ga8MKyg/1EZY5rr/NlMfa/Auv1zBcWFDbWJz8sCogzlnBF42eozH0s3YA\r\nR+sgsgtEBGew+YWPRzvzjip8eAY7nhCzlibPccAsX4uLuUpRrvkpsKFGusep\r\nBQ54yeMQ9cRO4xJrE5w4vg8pWcJ6N3Ff1cU=\r\n=PYl4\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.0.1--canary.8.d353094d51b9f17f2357a4dabb88993db09c2134.0_1650289219587_0.9108221571055644"},"_hasShrinkwrap":false},"5.1.0--canary.9.6a2ceb3fe870c1eb29eb939e10006179333521ca.0":{"name":"@sberdevices/assistant-client","version":"5.1.0--canary.9.6a2ceb3fe870c1eb29eb939e10006179333521ca.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6a2ceb3fe870c1eb29eb939e10006179333521ca","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.1.0--canary.9.6a2ceb3fe870c1eb29eb939e10006179333521ca.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-2k1AK8fhQ1wlISqk/DGtn5qLlGXz0zru5AV/Nli1mOLiVYAlL+rFgw3iV3jkkVjZRuy1ZBYcMvMKRT2tmsg81g==","shasum":"e9b838f46fc16eb2970c0e842191c46a94333a0f","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.1.0--canary.9.6a2ceb3fe870c1eb29eb939e10006179333521ca.0.tgz","fileCount":109,"unpackedSize":2326237,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDMbw0Uqz5Zen1XihFsRip75aMvMtgyYP2pdIou7FTz6AiEAt3ANuniRvBWN8O4nH0fiF8ymNnLbFdQEFjoCOEm8jvM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXm1BACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq66Q//fCjFLA2zpY/ry4AmqdTKlzvzDBLJCPFA00/BJsMjv7uzssnM\r\ncm7uNbk0YAMo5qYjkmbFjwYjWYsQqq15QPtGlOq9z6tmSKrKeYCaImjvrXRh\r\nOrH3o/pETwqFgYX8bqYP8EVQrA9VctWCJ4XBO6W5Dh+3lhe28hKZtCnx4Gq+\r\nQ3UZYidMaul21qggv9BbGiLLRPRIYQWAB8BHBgE24X9IQLOhkzhtoE1vrn/A\r\nm1im6xU03+QeIXYmydUKSo9wguBEqDhQ05Nwjpm//yOdBgkOnIqgiRF9z7eW\r\nXOuNPq82AhApDiBSlnhk1QQfoCm08CEIjCn0KY57T0lKYMxmRXqFliEm2ZpG\r\npBNj9gswcqc5dbCjoEZRurG0T1ndIBQ2NnsGIKtR07XfbntujuFtI71lRO8t\r\nsNyZPmU3dlDRKEsnTg5y9ISiQFWkz2ZwL2gE+Xcbk44gpPdVKaJ0neBgWHwT\r\nsa0wlPSN6RvVaXCHCsFJeLOHBj56TVZRSDo1vej/+t4gzox/AHfjpOUhp/tL\r\ndrxyaCmHgoLE2x8SHE3ltzprQ8rV0leAhbZBX6DowXOxACkhm8LNkiicphJB\r\naKWuqV5iF0WGU43TLaFzbzz9x25iLUFYiPH/TGA7dXkwPGvuxIXxLA4yWQ0Q\r\nT9gwHxqUHaV+HaLQ4IzEIBYE9qLkutnv6eY=\r\n=v9By\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.1.0--canary.9.6a2ceb3fe870c1eb29eb939e10006179333521ca.0_1650355521589_0.20876425096647622"},"_hasShrinkwrap":false},"5.1.0--canary.9.0624e179e6530344c354847689f2dbd59e11e7c9.0":{"name":"@sberdevices/assistant-client","version":"5.1.0--canary.9.0624e179e6530344c354847689f2dbd59e11e7c9.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"0624e179e6530344c354847689f2dbd59e11e7c9","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.1.0--canary.9.0624e179e6530344c354847689f2dbd59e11e7c9.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-Kv4rtPSTauDKDbkL2ESjYCsbB6GGuBpv7IzbSppHTZjoYLZkO7G26RCKegO12FcSOuJonG8qrlXsJ+/fCPfwIw==","shasum":"a2c4fb1216f8f3242c9abf0cf79ca767fee01b09","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.1.0--canary.9.0624e179e6530344c354847689f2dbd59e11e7c9.0.tgz","fileCount":109,"unpackedSize":2326237,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDToTRRbH/RCzjA/Jf4IwHS6deQE0DJaXMG6JjWAADD3QIhAPRDsRdeABgl95jXTgbY/bhrwISlvNguyc/VEKp/L0IK"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXm3FACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq2QBAAmgI1qxBMi1CiRdcu5gMksbfkVGAps3kXXaxuSFOkB/zwG8oD\r\n3DOf+M4hU0EcN+fpKUQHTK3uJnxgotYWMIWdSuu06kRBu3CHBz474upcTL5e\r\nE1Ek9Uiy2tVcQkpnbUryfcqKoZjrSC9PHjY4QlyAo4RRzW33doRCpdYZBBBP\r\nKsvBzFAayHrtuB1Sk1Mg1hahGubNrofXPQdNoRCnH7sGZoA0h06LowQTeypy\r\nXAT72X2dDmta7vIGmiMXzb+ZeLvdmuPhqA9sOkBMASRVvSH6eyoQUH0zbusa\r\nmsTCo6d4+XymPgbbsYijg4Pwj/sxNke20paOzS1dlskVkFQ9OzuYLZuOpWOO\r\nSMFec3yDSCK1RS+YBjYlT6czq9P9EueYAH9yKXk0D0/lw1IpWTXu//FpdCvZ\r\nRr45/Xsg7XoGFbqVDnV/CmzX5sSvBFYmgY3gZr4hphgNL4L+WSJMBBkiDtiA\r\n6CXwrXL65PeoPattZpUW6rCG9kXF/XuEroQE7gPZjrmq2BfUFZydayXpHMiy\r\ngksLoRqMTdu8bU8ZqDf1PwJjW9QSFJkASY4OVGZ4wnOurBxse0LqEuALS+fj\r\nRvreeoMpeSWdKGenyvonLQQwYeF7fXyoVAPqrwhGoCVwRPakgcSRb3WAVRSW\r\nvaXMqFDCs6P3JGO3pMpH0wv01DZefkFgfAU=\r\n=cbsX\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.1.0--canary.9.0624e179e6530344c354847689f2dbd59e11e7c9.0_1650355653164_0.7270929638908032"},"_hasShrinkwrap":false},"5.1.0--canary.9.f1608f5ee0108c3c45cc36fcc118c79535bb97e3.0":{"name":"@sberdevices/assistant-client","version":"5.1.0--canary.9.f1608f5ee0108c3c45cc36fcc118c79535bb97e3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"f1608f5ee0108c3c45cc36fcc118c79535bb97e3","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.1.0--canary.9.f1608f5ee0108c3c45cc36fcc118c79535bb97e3.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-bjFoTLJkLTfZ6x/OcNvOH2sWt8uG6BdXZkFgIRADcDwdrefggEH9fsVJvE67FMui+Xp3CfRevTpvtLKMGB8Eag==","shasum":"8ba66f16966ca447c6aa392cb87dd03906f6d1d6","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.1.0--canary.9.f1608f5ee0108c3c45cc36fcc118c79535bb97e3.0.tgz","fileCount":109,"unpackedSize":2325954,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEskEIZt4ZMdXmFU4lVslVp23/d2KiCz7YOKO75FRu97AiEA4L5QdgEQWjLkf5FerR4SQNDFk5gk15V7C2f5leVa/cw="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXm47ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqUIw/9HM4N5h7hiszrPBrDQ04ze04BgmqIt6/ouV3cKj5cWnbBVhuz\r\nCYZd8fMAWUcABEO7l0JIHXW+OhkMaX8iV21g17gj4MHHmZ87FUkrOv5OdZJV\r\nLlb7fwpKizZDSThKwCutRGKtrlc7bdGbOyH/5FlLeTJhNQ1i2ijvPzjc3voC\r\n00XGFIQyYEBz60uF9ToPmg3UzixLPj0dGGgo5w5GklNGITgZHY6pSfoFiAw2\r\nXFYWTg39MyYv4rEnHip6BCPFhoroF6VGxVhQCoiNIc7nu9KmcqYfvmOCCQqZ\r\nqpygu6i9lTfB/+9+PTyRbWQCh83LzqJ75WxKasDbiUbXGzj7ojCTGcweco7q\r\n9IqcUrhGnNinx9OYQY/iW91ZwEPMmt3tdt9v1bnSYqHB71ctgNEUVkffI6l0\r\nFQxotI2Gpi/mk7fribEgVMtJMoftzxhjt6woNGULcYVFWseiBp691W7+03R2\r\ng0PeaYIxotajEU+l/9oBAfpyAfyU0rqzbyIW/QcVGtQO5O9EQmh1I+zAvvMK\r\nU47scbmVt26m4LGwrgKjH1Y6mQDr1HDmTexjU10z1yW0PVhJijlmFPzN5Tns\r\ngdDx+6rLr38M1coAgTf2/MWaOV7Ly0LA8PtgQomSRHVENoYZCKrhfIq4RgEF\r\nnAPFmXLJLSMIAz2FMRd2I+ugjnESXoKxZcU=\r\n=UgN8\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.1.0--canary.9.f1608f5ee0108c3c45cc36fcc118c79535bb97e3.0_1650355771171_0.12862718790926486"},"_hasShrinkwrap":false},"5.0.1--canary.8.a940894e6693c7792ae329052ac73fbe4f179f55.0":{"name":"@sberdevices/assistant-client","version":"5.0.1--canary.8.a940894e6693c7792ae329052ac73fbe4f179f55.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"a940894e6693c7792ae329052ac73fbe4f179f55","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.0.1--canary.8.a940894e6693c7792ae329052ac73fbe4f179f55.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-qG79jK94pvT2yLTeqpeLaJtATp64SaoaG2LWw4u8UELvQKMT++MdIkQ01sFzhuJaMgFaPt//pRJW0Ay9GwNyjQ==","shasum":"b024440dcd623b87333b2ed296d2405957a9fea3","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.0.1--canary.8.a940894e6693c7792ae329052ac73fbe4f179f55.0.tgz","fileCount":109,"unpackedSize":2119318,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHDoCvyCpvyho2o/ZDDNLoK68IdaZU7e7favLNEJgxwwAiBVz9T1SWxfSJ7xbfeEBcM4hz6cKooOMG7g2N0I8sd1Tg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXm/gACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoQzg/9EEt3B4O8rqAikUXINHtlC9q3LrAzuhQx1h7q8WpeDGtRszEX\r\nywKw4TWwoBS9ybICZLTzKYB6pNxAU//sqmNzN8OsTRxxD6hjzs6l0T7VC/RZ\r\njxpK4oqbBjHB+aKtNhiCdPXVm70R0atVF/sUbGRJMplkxsr5w9p0hLtvglRz\r\nGac0cjpZE2T0fgoXQ9YjI82IYSQmXZwSV94sJwHhqTs9TzclkM2xD7I+ttOz\r\nZ/Fo3emhlVvrYvfHLKrcfAStsjYrv8x6ToJ9kuU6cNNGSI7L3XlpMtTDLZGo\r\n0wnCa6VZw6fqeLIrk4EZvco8Fx5tqkv+QuDHqAzDvGftNZX4DAhZ9D4P4A3W\r\nOvYFDnpCsvpobeTZuQ2G8D2U0HwI6NXIuEoe2GpJFopr9y4sRLJUgLpKeAPY\r\nMgP0KpWIPuzgTJfQ7zI1lJucxgh6fYEh/qUwvZooFCCMnb7jDAjP6CBO6pkc\r\nv80hvi2v4DWYiQCE2GjIHj3B101VwfkZYtYhWMBudU5oHG96PGwlGaeSmMXL\r\nBiNEqEmaREC5AQE3ve2rp9TOfQ4WkywbJXcBnkZ9Oztr9Mrx9j3l3cdVD/xT\r\n4iXtE5UO/FV1c/GSilo2UAGrve/zFZYUkpKY+IJWEnMD0gwerw+hwPTNdi3s\r\nPOh/94KfGqyWy2VxTjF4Pm/xD4ayIPnJMzA=\r\n=ZR66\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.0.1--canary.8.a940894e6693c7792ae329052ac73fbe4f179f55.0_1650356192335_0.5969145821487927"},"_hasShrinkwrap":false},"5.1.0--canary.9.e75e9b634ada4b78c12180a3dd881319850ea45f.0":{"name":"@sberdevices/assistant-client","version":"5.1.0--canary.9.e75e9b634ada4b78c12180a3dd881319850ea45f.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"e75e9b634ada4b78c12180a3dd881319850ea45f","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.1.0--canary.9.e75e9b634ada4b78c12180a3dd881319850ea45f.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-8Jq13HYjCgyNYBMEIUmTELEi8f4ejDtChy9CL9eiMU856f8r4PRYZsVy7y6EbgyywANTIGcpziRgU8IsSzO4Uw==","shasum":"2590ead7b5e3631396782d382fa9cb8c464ca02d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.1.0--canary.9.e75e9b634ada4b78c12180a3dd881319850ea45f.0.tgz","fileCount":109,"unpackedSize":2325954,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFoYGpNbbWNH0qIhD/AD8JjwGAYFJZmrF9aUuvEVw/lJAiAna1igVzYt1IFN3OlcjhqIt8F9uYpu0WtkiarcACIekQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXnPoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq7qw//RSJWC9R97mQnj/1uNit0dP/7G9+QklsRcbyvD03fusx9ZVs7\r\nQzcpzp4ttYHGdMJTq6EUE+eP8mpgQvd1aZMYQhFI5nSPBTPo7ewurCiwBhE+\r\nzPy4jUsVIDONlYsEdopOWbSTtRYsKTdm7tKsuAhnoE3RxtU1hDr0agQHq4By\r\nYueA0WlZry01Mw3ucDlJwiT2dS1CglFMRNAdWXLjkQLJCuxYIXo0inzt+vTb\r\n3+auCNqcmch8PJoNMARqyiEBf07azWiIJwsZHdNqH4fbGkJi6VcGRbjbI0DU\r\nJKzv4K8LNHc6W+9jd+KdR0UYPRDBVa7eRRTwdcNDfeenKXuSYMtXXAPS5zaU\r\n7R0e90KeFvXqedxMcJMOuAwkmZ3w7zOAV+AFnx37SfSZyyjNTAog5HWkiDTv\r\nwUmKK98Dgzg2Zre7YyCLBDC4MI+9TddcIgLm415VSpoO2wyqFxk6JB76xljI\r\nq7ZMaSEkqZX66n43bG5hS6ApL4+pDHYfzM/+qgINL2O9mk03HCFDEwT9hfoT\r\nJqhw1/9OH1mgkdx+/qsa/Lh4jI6KYzcaCLRt+4/Kb9YzXUK8A7JWu26XXG76\r\nXqHmSIEgEIt4Ix9LHWKMQgnvNK0gM+FK4jdb2lPBg0FiFoPwNL3kdivhCOhS\r\n6z9T8RvoeL2fIlw/W5lBAocsASrCMi+5dvI=\r\n=1cJX\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.1.0--canary.9.e75e9b634ada4b78c12180a3dd881319850ea45f.0_1650357223800_0.39677320746062406"},"_hasShrinkwrap":false},"5.0.1--canary.8.38abe1837f16c59d945fe2c3bc8dac75a8051087.0":{"name":"@sberdevices/assistant-client","version":"5.0.1--canary.8.38abe1837f16c59d945fe2c3bc8dac75a8051087.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"38abe1837f16c59d945fe2c3bc8dac75a8051087","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.0.1--canary.8.38abe1837f16c59d945fe2c3bc8dac75a8051087.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-a9WC6Szy9B+XEVnw385uTiiJkBdAIGs3MVdW3WPU4i27YfQk5rdXhAKeTJ/FPkN/AsuREz35PFYcbItoT8G/nw==","shasum":"f6a27d0afd988e046954601b09430f0fc255e372","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.0.1--canary.8.38abe1837f16c59d945fe2c3bc8dac75a8051087.0.tgz","fileCount":109,"unpackedSize":2119124,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvnQv73pxmLAco0yN/we9vQZVEvhxbjw8YAHrWYA6G+QIhAIOj2Jyw/4k3fmVdNcysu5qSL/vTVWFeFjrSeSAxRt8w"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXnRoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoAxBAAm78xDLRPbitG+HmNIgzDAorJ4VFDHgRqj0vBheclqa9AnPRV\r\n4oQLSUotzsvvY0BYR9h0HRO9VlH2Yvc/yVLn9GP6ktzwpAIK3tZQzvORfrYJ\r\nZ6gJ+g3Of9Yd/DaAIkiDKA7pbqIk7N/FCsMamwvaWtJgOOzhyXrFOue8Nx1Y\r\nP2310PMq8H/rbG51cFdb0aTBgZ3PjvbU2/j3/SWcXDSDLCePO9EtkEHS5odr\r\nUNoKYqa7a4rmJpUPxC7dilnOJEVChd2KdW+hX2zyvW7qZCcM1MAOREFxdp3L\r\nAQnLKsCx43mlwzD7bZ5yY3d0/I5ShKRh3srs6iOq0RuZ/VLOZWVI9bp/Wyao\r\nlMhjMK5wpQ2xcpXlv8DgCuM97cLy3Xysd6NrCrvdMHfeySPTumqcOL5O2lv+\r\nDwpMv6hJG4BRU49cHfjxuII+EzhOr9zJ4NucGLz30GFiSYn2Q4Ji6tcBdEAq\r\nKHYGKxBuDd5SYCNBjB5JA+xhtECEX+LLS0XgS+GTSiFqSl5gOYDApSbpQcNY\r\nN6LBmHRRBZjLfahF8/h8eHxZN65JvA6Lu6R6bNGxK5MS7WI8FqPchz5eOAet\r\nXfEQrLQ2LqU9oF7QXKkYfWgckfUQVPcJUBWu+7MrQNOTN2G9CONwfthlrIfu\r\ntlcy2MG4oq2KeZI9S0WZ+bZjFSDPLxi4jqg=\r\n=qjUx\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.0.1--canary.8.38abe1837f16c59d945fe2c3bc8dac75a8051087.0_1650357351943_0.36812487400271277"},"_hasShrinkwrap":false},"5.0.1--canary.8.1ccb713bb797fa64883d89d5fcb8ca7a5f83c36e.0":{"name":"@sberdevices/assistant-client","version":"5.0.1--canary.8.1ccb713bb797fa64883d89d5fcb8ca7a5f83c36e.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"1ccb713bb797fa64883d89d5fcb8ca7a5f83c36e","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.0.1--canary.8.1ccb713bb797fa64883d89d5fcb8ca7a5f83c36e.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-flh2ZcUyV4aUQTJdEHg9mmtX5DeQqD03qNsW8BxvCpT54QCdRKdQKm5wA3Jjb021AS7XLSn7V46bEt+xbSqlRw==","shasum":"a9645696f364a8a449c76c8d820f290683a89c4d","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.0.1--canary.8.1ccb713bb797fa64883d89d5fcb8ca7a5f83c36e.0.tgz","fileCount":109,"unpackedSize":2119142,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGELB1LqW7wU1/gTWFSrQfIy+BYCiGzGpFbEijbc0wyAAiEAg1RbZSysE3Z8wPG5x5QJCm/DVjYkVJZ4olt6cCHRMbY="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXnfFACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpfxQ//bj/7khM89yOgO67ElKCWQU+28KOIjDIz02cRABIEdaQ9AhkZ\r\nhAIoZ+SPffszUyjAMLaCNJ+k5/LyrtIx4KbZr/dK2TWLyMMV9BmGyxbxrDGG\r\nmflJwXhKixlYoVH7dbNJgm4RZgyZPD/PPT2xgU++fQYD14RPFDH2/M5AnBVo\r\nqCBXkCNzlaXM5HVRpKlw7qk1XGwgovD9cRuapT4oJ/FoyczFb9rZTnrAhc99\r\ndBMuB4rb5jM9iKHDSHTKchoGO0oIQzrzgLLG3sMHwJIjVFBRz/qdk9Q1SkY+\r\nG8CJoMKitHqg/EClSeVFykqvZDeQrB69lOCdY8SST8B3K5Yy/DnwRndtXa2u\r\nl6xfWvg0G91T2tuezPiYJwWsLnoeBIBwH6jgkg3TraQco5IrU3g3Ai9q/5//\r\nvm2O29VfuTM/LZuI9eNnOKKSb5tRzGhV/a7zidb6zCyBvv9vcQfT8oLGCX6w\r\nDlI8vYxw/bjhJnyiZ+rsTpPTzyN6wv4nDE2pFsb1LgeHYGmCsdKZ6ke3KPed\r\nUmKT+L7LAmGJIZcarGc8d6V8SUG9zoxIAv1pHRn4uev0vqhRlRbokNUA3npP\r\nvwImgnUmPU+APp5xLjn0qe+gbCa9PulmyeTq4LSBrNvewzDG5RbA2bu5NJ8Y\r\nWtjG62P2G+ml4RnN8YE0dB0GOr14Bseazcc=\r\n=KEEo\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.0.1--canary.8.1ccb713bb797fa64883d89d5fcb8ca7a5f83c36e.0_1650358212926_0.7268629822154904"},"_hasShrinkwrap":false},"5.0.1":{"name":"@sberdevices/assistant-client","version":"5.0.1","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"cd934954fa6136b37d6f6bb4fac5f19ee73b918b","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.0.1","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-nYlx77TdeTOaWjNTgruPQLhy61wHe930H4FpppUEOAKkW8+8AFCCWjRyepFg7YhK+F8o6kv4bceIpq5K7rLV+A==","shasum":"c909c687f2719d059ff507d4a6e0e4c9ff5f2fd8","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.0.1.tgz","fileCount":109,"unpackedSize":2119010,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICegxFipZA9bNnfLg16jveYEw+TIY0BpTr6bFJoJgf0LAiEAjzFk9XsRyV2HSVRSvTcVghkDpeXaVx2k7bZAeBviBGA="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXn9vACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqS7g//dNAvJ1LbK3E8ZBuBPBHPmNRu6pKEMv2D24KnJjS9Ut+kgkit\r\nuQomeFsF11CEzAlAsLTQmocCw5NE+DXE6IkAtjIYVtFqO2KZt7fOcvFzsojh\r\n4m/dUMzKaN79dQsxtehNoff3RwyRVwA2IjOT+LoMU/0YWNlk82L77i3IooU7\r\nm2prMXJTqpOIEbsKR/p9vixF+s8baH5ArpKMBNG0NZp3ojt3ewSPLBbGXj7M\r\nqU+p33Ip6z1/AA5LBH7UuJgCsQFFO4iwiMMzQrB36K+M0d4gzedhaoA4t3PY\r\n87C5pO5YMhEzTaUGAeNsE0L+BSRbkHBZsN09tjMPtMiYtVDDHw2wYEQNuBcL\r\n/S/0GSf41ykMPlWoVKJ+15gcLDgqWBcH/X55en8LY4W0DPDC49BCbk6LmwAT\r\nqXhdBHXl7tcmWpk4YBzPuK5oDYOIN6lHMGmVes/aTnvq2B9G/RsAvk/HhJxM\r\nevLOdcQz6Ls6rNdXGeMkEQRPNy4DpS69EQRejgsw6/gDMTxII81Vb24HGzbC\r\ng0ljDnIWhJimbhRy3i7dgLnFtiqaRu3LF9/Zo+i9vlmFSExIQuGdWAHSBv5B\r\nJTi0JSse0Gf7DuCqE0LHi+cr9sryY3gNBiqhgtOonYYk/He3pmlKCzbpxq6o\r\nUg1k6kYB7ejhHyrF1+ppQM1W3iC5A7XvZnM=\r\n=SOVx\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.0.1_1650360175347_0.5103845450382491"},"_hasShrinkwrap":false},"5.1.0--canary.6.6cb0d2c05b1b21c8391789a76cd71dd9d69c44e3.0":{"name":"@sberdevices/assistant-client","version":"5.1.0--canary.6.6cb0d2c05b1b21c8391789a76cd71dd9d69c44e3.0","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"6cb0d2c05b1b21c8391789a76cd71dd9d69c44e3","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.1.0--canary.6.6cb0d2c05b1b21c8391789a76cd71dd9d69c44e3.0","_nodeVersion":"12.22.12","_npmVersion":"6.14.16","dist":{"integrity":"sha512-IU/fB0oYWQOGUT2a1QUCSoqt/FoEplyCbsRnV75bB9Q9YCzORQPIFDp5ZzwZYfxuGuDnRaY/jrPzrMs0noD/ag==","shasum":"1d7ee2b45996cee2180811ad5bd58eeb9d3a5e48","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.1.0--canary.6.6cb0d2c05b1b21c8391789a76cd71dd9d69c44e3.0.tgz","fileCount":109,"unpackedSize":2125490,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC1JZJalFqF525HpBomzt1kWdvJlByhyN9egWdFX5SGJgIhALvCvSOAzYED3spjR3du/AlpPHRHgunz6Gv0bwKU6N/b"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXoyQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpFfg/9GNMUQBuFlwdjoN9+fbT5aIz5dBGeK8FLXMpE135h9+CnOViW\r\noRzbxB24LaQS2x7TKL3Iz6AkjYIYGb4+4eDNAlV3dvLUUxLuRFSrhdVpcY8v\r\n4LtP+o3UmTgTbH5E3yD6HeleIvefFyX9mobEfJ8Kg0QAzEuYLzvBc4KIULoz\r\nnLQu6syx8RGPn2i+pX/BTsn7BkwHZQlaJHUHp8gYdzxEPYzAXGrd0j7N3x9A\r\ntCSgJp7gIOgw0M6Xv5xz7+aUEmiFBRw2XwPVs1DVhPlHPy3i0Pr9gg0XP9QE\r\nSxFV68OAXcY03CGnOUTHXEL0NdXhPRtdqSnToarxv1u1e5cLvmL6F6Nx5QZM\r\n3AId3ryViJ2lJfrOpfWvoh3mjL2kx49PqYk1JFhKhOwDPS0uZotTkjQecxg8\r\n0BGs5mAc5WASKUZBalp+v6AZ15t6HMJrScTKUr4x7COfQfU9RQiULZ+3FF1p\r\nxHZSEBIOwZNhiNUUMRo6IZ5Msskbv3oyIOLitZIgPiaZZt/HpxucCaRiG1yy\r\nJ+RqU+5g8gdqx4DdOV7R4Y1Don/Lgs4BJtT8vOoghUoIcv8uZzxflkFzjL0r\r\n80XZj+vPenLxjGdCGXvIVfuYLb4VzHy4+5hmWePqVQ4aLAnZeEqq0jnJM1Ln\r\n94+gn+CuAPHwujpN0dtfsdNfvHR5EeMmXWA=\r\n=nKFo\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.1.0--canary.6.6cb0d2c05b1b21c8391789a76cd71dd9d69c44e3.0_1650363536241_0.9149488986874659"},"_hasShrinkwrap":false},"5.0.2":{"name":"@sberdevices/assistant-client","version":"5.0.2","description":"Модуль взаимодействия с виртуальным ассистентом","main":"dist/index.js","module":"esm/index.js","unpkgdev":"umd/assistant.development.min.js","unpkg":"umd/assistant.production.min.js","scripts":{"prebuild":"rm -rf ./dist ./esm ./umd","build":"rollup -c","prepublishOnly":"npm run build","release":"auto shipit","proto":"pbjs -t static-module src/proto/index.proto > src/proto/index.js && pbts src/proto/index.js -o src/proto/index.d.ts","asr":"pbjs -t static-module src/assistantSdk/voice/recognizers/asr/index.proto > src/assistantSdk/voice/recognizers/asr/index.js && pbts src/assistantSdk/voice/recognizers/asr/index.js -o src/assistantSdk/voice/recognizers/asr/index.d.ts","mtt":"pbjs -t static-module src/assistantSdk/voice/recognizers/mtt/index.proto > src/assistantSdk/voice/recognizers/mtt/index.js && pbts src/assistantSdk/voice/recognizers/mtt/index.js -o src/assistantSdk/voice/recognizers/mtt/index.d.ts","cy:open":"cypress open","cy:run":"cypress run","test:cy":"cypress run -b chromium --headless","lint":"eslint --ext .js,.ts,.tsx src/."},"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"keywords":["sber","assistant","smartapp"],"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"license":"Sber Public License at-nc-sa v.2","dependencies":{"@salutejs/scenario":"0.20.0","axios":"0.21.1","lodash.clonedeep":"^4.5.0","protobufjs":"6.10.2","uuid":"8.0.0"},"browserslist":["last 1 Chrome versions"],"devDependencies":{"@auto-it/conventional-commits":"^10.25.0","@auto-it/npm":"^10.25.0","@auto-it/slack":"^10.25.0","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@cypress/webpack-preprocessor":"5.9.1","@rollup/plugin-commonjs":"^16.0.0","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^10.0.0","@rollup/plugin-typescript":"^6.1.0","@types/jest":"26.0.14","@types/lodash.clonedeep":"^4.5.6","@types/mocha":"8.0.3","@types/react":"16.9.35","@types/react-dom":"16.9.8","@types/uuid":"7.0.3","auto":"^10.25.0","cypress":"7.6.0","eslint":"6.8.0","eslint-config-airbnb":"18.1.0","eslint-config-prettier":"6.11.0","eslint-plugin-cypress":"2.11.3","eslint-plugin-flowtype":"4.7.0","eslint-plugin-import":"2.20.2","eslint-plugin-jest":"23.8.2","eslint-plugin-jsx-a11y":"6.2.3","eslint-plugin-prettier":"3.1.3","eslint-plugin-react":"7.19.0","eslint-plugin-react-hooks":"3.0.0","eslint-plugin-testing-library":"^3.10.1","file-loader":"6.1.0","husky":"4.3.0","lint-staged":"^10.5.4","mock-socket":"9.0.3","prettier":"2.1.2","pretty-quick":"3.1.0","react":"^17.0.1","react-dom":"^17.0.1","react-scripts":"3.4.3","rollup":"^2.33.2","rollup-plugin-copy":"^3.4.0","rollup-plugin-replace":"^2.2.0","rollup-plugin-terser":"^7.0.2","ts-loader":"8.0.4","tslib":"^2.0.3","typescript":"3.9.2","webpack":"4.44.2"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"sideEffects":false,"auto":{"baseBranch":"main","plugins":[["npm",{"setRcToken":false}],"conventional-commits","slack"]},"gitHead":"b7d4157d8c0bbeb175bc653ef1ae58ba931c21fb","bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"homepage":"https://github.com/sberdevices/assistant-client#readme","_id":"@sberdevices/assistant-client@5.0.2","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-8fYLqyOrwWQjzszY91pxI5h3EHkF9lXOJghhGh4BgRVQOu49kc5RytaVJ9D//0GwFP7WiO1Ea58ZeZFeMqIOVw==","shasum":"e6e3a02b7de64551c3cf7d5507c38ac5f22aa0e1","tarball":"https://registry.npmjs.org/@sberdevices/assistant-client/-/assistant-client-5.0.2.tgz","fileCount":109,"unpackedSize":2119206,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIbSURsBpgcAJYBxl+UzWg36JtSy8es+t4rRQRMbbMagIhAIIc5wLWE0tAQ/EOubnABxGLZDHfxGilKZ5sGsFyOlXJ"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiYUhHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpFLRAAjgHc9ISJqOAeVTryXZHUMBvHmCFhtVD1xM4gtVzzA38efXa3\r\n5DbkyNLXjq+cch/mrpWv+ckaupV41bsK1nEhy9Gm9vhG9Fe1JCcIKkPp7Y/w\r\n/t/Z2e8mLXM/Svcs5AEVpQTPxi6IPiyk6JeWGm5Rc6Om9ogxeikKNIAsV7JY\r\nixHnjuZ3ola/coZp/9ghD5TLOEQBEjOYvxG/8aRKjKpbtKr8S6Q3glF2uFXB\r\nBCsh1gt/HskTSRv+AVfNOfCK9wFNrxePeKxHLUg5ReQiw4/1HWJhFLIM8Ykr\r\nJ7zG98V49C+I1sjIcQhijEYH6pDZmwuBpZBM0czLukivH96AbL9r/XcirDBU\r\nxlyYJqFsWQVH1i/R0inHs/UJ5rnuFP51tbdx/N3OzgQTyyS74elg+WFvwH84\r\nUqD92cHi65RBE6/E6177j+Knj4B/TVYBhWWsACVNPxU2xKqWan1iP9AFi5H4\r\n69MO9fT1kHWg1T80MXmtyMsxCRQDqkAUgQ8cUE8RoNAozhnzETnvFfToQ5xQ\r\nnlC1E+Lr3HM2cBUEz7PkyWYj2Hn3KX0FTrNAdqAtFEo88tFm54fhpH5nwKWt\r\no7XPyOjNb+2PFgdDYSWrir0mNIAmkGAH2PuVYpTNMLHrX9o8+XjOxdv+WdlR\r\nUNDB4oHhd0bPd37oA4ejgtv87RE3HLM6z/8=\r\n=96JD\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"},"directories":{},"maintainers":[{"name":"awinogradov","email":"winogradovaa@gmail.com"},{"name":"turanchoks","email":"ipuncho@gmail.com"},{"name":"sberdevices-frontend","email":"sberdevices.frontend@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/assistant-client_5.0.2_1650542663727_0.016165880302878577"},"_hasShrinkwrap":false}},"time":{"created":"2020-10-08T12:04:13.353Z","1.0.0-rc.1":"2020-10-08T12:04:13.470Z","modified":"2023-01-13T10:33:37.213Z","1.0.0-rc.2":"2020-10-09T10:43:58.848Z","1.0.0-rc.3":"2020-10-13T11:10:47.096Z","1.0.0-rc.4":"2020-10-23T09:41:39.882Z","1.0.0":"2020-10-23T11:57:53.080Z","1.0.1":"2020-10-26T12:52:41.262Z","1.0.2":"2020-10-27T11:52:24.358Z","1.0.3":"2020-11-02T09:14:14.151Z","1.1.0-canary.28.0039888976b7c4d0699434907891c4e88b639e7e.0":"2020-11-03T06:09:34.752Z","1.1.0-canary.28.e8024722f14a1cd664ff356e2410446479f3d54b.0":"2020-11-03T06:24:18.480Z","1.1.0-canary.28.ff63d06e164fa2ddc145ac23fdbbddeb98343c9f.0":"2020-11-03T06:28:52.070Z","1.1.0-canary.28.2b33222b202dc51c90c49b88daeddb053aa47458.0":"2020-11-03T11:53:40.440Z","1.1.0-canary.28.c89484a6f50a3216ceb49b7e8f9b0c337de8b899.0":"2020-11-05T08:21:45.259Z","1.1.0-canary.28.7e205355c78b9375dbc6f799cf47f536201dfb5e.0":"2020-11-05T08:26:47.776Z","1.1.0-canary.28.13b9cb7b0ded987d2540334b42c530f888514d74.0":"2020-11-05T08:31:03.690Z","1.0.4":"2020-11-05T10:30:42.185Z","1.0.5":"2020-11-09T07:52:03.755Z","1.0.6":"2020-11-10T16:31:41.464Z","1.1.0-canary.28.8847c2c108c0d53c38ae84dbae2c2d3661879e36.0":"2020-11-11T10:47:58.323Z","1.1.0-canary.28.fd9cb485eed455a9e6b47cd80b581d88d0b3b144.0":"2020-11-12T07:02:19.725Z","1.1.0":"2020-11-12T09:56:38.926Z","1.1.1":"2020-11-12T14:33:42.821Z","1.1.2":"2020-11-12T14:52:10.435Z","1.2.0-canary.28.37f04d67478d37b5a6faf8381a0082f32783ae95.0":"2020-11-13T10:55:50.958Z","1.2.0-canary.46.35f944b7f71f70699a407c0fc41749c063fb49a0.0":"2020-11-13T16:02:43.038Z","1.1.3-canary.47.c977db33a85d8c0059086e42bf97ffda2a8c0d80.0":"2020-11-16T08:07:00.502Z","2.0.0-canary.47.36b72a0145633e6dd0859a486cb4afe4c660d3d2.0":"2020-11-16T08:15:58.765Z","1.2.0-canary.48.5300a8567f6a164f0926452edd09df73907cf1a5.0":"2020-11-17T09:09:37.042Z","1.2.0-canary.48.06759b70740bfa05ae3b420701b5959a5bfb7b69.0":"2020-11-17T09:23:39.760Z","1.2.0-canary.48.2d58b78418ce0d9f4ec58383bb4a9eabebfe402e.0":"2020-11-18T07:04:15.851Z","2.0.0-canary.47.3f63e50bbf0cc6e0a4a744e6037012680fe05216.0":"2020-11-19T10:24:29.958Z","1.1.3-canary.51.eb7663ab912681c9921b58b8434133cbe9f8cfb6.0":"2020-11-19T11:33:23.087Z","2.0.0-canary.47.23999220acd326b1b94bb988a8dcd653ea564075.0":"2020-11-19T12:26:42.725Z","1.1.3-canary.51.b066573.0":"2020-11-19T13:30:45.132Z","1.1.3-canary.51.b066573.1":"2020-11-19T14:04:20.935Z","1.1.3-canary.53.fc82d53e50c23f0fcee8a894468fb30e1f98fbfd.0":"2020-11-19T14:07:37.201Z","1.1.3-canary.53.e4b7570f10a82dc107b62b4222a1c23b1807552e.0":"2020-11-19T14:14:06.942Z","1.2.0":"2020-11-19T14:16:43.763Z","2.0.0-canary.47.8d47dfb7d4df197505de691b2762d483cddcb538.0":"2020-11-20T10:19:11.012Z","1.3.0-canary.54.784dfe7defe33bdec6485882e9dc64567512bd5d.0":"2020-11-20T14:04:14.945Z","2.0.0-canary.47.c89de6ac5a5aaf33bb22f5878af6e2b5d798ad78.0":"2020-11-23T10:59:32.808Z","2.0.0":"2020-11-23T11:02:01.979Z","2.0.1-canary.56.77d9ddf0032ef997caf6b9132fe21d359ded30aa.0":"2020-11-24T10:25:22.763Z","2.0.1-canary.56.9d7ad87acf790f295d1353db76998f74538a020c.0":"2020-11-24T10:39:41.620Z","2.0.1":"2020-11-24T10:42:40.777Z","2.1.0-canary.54.6ec2b028f032e82d874ccbe772a73622337614cd.0":"2020-11-24T11:50:33.853Z","2.1.0-canary.48.ea0c2f160930cb103ea97ab3254ac0298a60de50.0":"2020-11-24T13:23:02.569Z","2.1.0-canary.48.8b9ec1c1c05e3931db0d09310bb38b0595963367.0":"2020-11-27T07:54:17.064Z","2.1.0-canary.48.c9b4bd896244a171c6aa949c04c11565d8486fa2.0":"2020-11-27T12:10:53.892Z","2.1.0-canary.48.6b2c2bde242df6b5fc61c16f3265c389e9b5c041.0":"2020-11-27T12:12:13.351Z","2.1.0-canary.58.03ac302471e41b44fd8a93a6dac19b85a3693042.0":"2020-11-30T10:03:47.613Z","2.1.0":"2020-11-30T10:21:51.991Z","2.2.0-canary.58.f73e9ac794e22bf14a958314a79b9a47704e1438.0":"2020-11-30T11:04:03.441Z","2.2.0":"2020-11-30T11:07:00.115Z","2.2.1-canary.59.5e777c992b8bf1d16c3ebee65339b06f53dd4fd1.0":"2020-12-02T09:07:15.625Z","2.2.1-canary.59.512fa8aef00f263236e660625d2208293d6b4372.0":"2020-12-02T09:08:27.674Z","2.2.1":"2020-12-02T09:11:29.386Z","2.2.2-canary.61.7e255dd5af0963b445c3df1f6de5bd869fd130c9.0":"2020-12-03T12:26:38.629Z","2.2.2-canary.61.6fa0c86f0e801048de7559f882ebed9e26c2ef4c.0":"2020-12-03T12:27:13.600Z","2.3.0-canary.54.57b4fa4d7264c6071f574dc1dbb73735dfb2fb1a.0":"2020-12-04T07:23:30.035Z","2.2.2":"2020-12-04T08:13:11.406Z","2.3.0":"2020-12-04T11:57:26.064Z","2.4.0-canary.66.c7b2b744b76765d7f12017a5b2ee7da64042847b.0":"2020-12-09T11:58:53.357Z","2.4.0-canary.66.9cbe72cae1f80086cd3a63695f4056250fffe5f2.0":"2020-12-09T12:14:18.844Z","2.4.0":"2020-12-09T12:20:25.026Z","2.4.1-canary.68.9c1e7e987e4fed8d01f7b732e1e738ebe8cd252d.0":"2020-12-11T13:37:43.445Z","2.4.1":"2020-12-15T09:13:04.319Z","2.5.0-canary.69.cbe3992c562bc2071f2b1247aa88c74fa1039f10.0":"2020-12-15T10:13:50.008Z","2.4.2-canary.72.f61261ca3bfc7f81c3b27a66b52c2fabd0b6b1c1.0":"2020-12-18T12:57:05.672Z","2.5.0":"2020-12-18T13:10:12.319Z","2.6.0-canary.73.5f3fe2717895d24dd7b02404eab6d6b4948ccf33.0":"2020-12-22T13:43:06.819Z","2.6.0-canary.73.32a4e319535a88e140d62f8431916719eee4f971.0":"2020-12-23T06:54:06.756Z","2.6.0":"2020-12-23T09:26:18.540Z","2.7.0-canary.74.d8f42e3013406e708ea6c932439aa79a9e5b1ba8.0":"2020-12-29T11:06:16.867Z","2.7.0-canary.74.26d827d71d0af7142af7cfecc7d2a44d385cdb15.0":"2021-01-25T09:49:19.294Z","2.7.0-canary.74.c934e4283921c6cd31fa3d2a153b5d9f8f49bb0c.0":"2021-01-25T14:00:11.754Z","2.7.0":"2021-01-26T10:14:49.451Z","2.8.0-canary.76.082fb972d8eff6de1586abeb923a4f7190beabaa.0":"2021-01-26T11:17:46.114Z","2.8.0-canary.76.0f076a5f9c499380f3beea4be7174b87a6765a57.0":"2021-01-26T13:44:08.856Z","2.8.0-canary.76.9955484cc76eed23c56d25be9b59366d9740cae3.0":"2021-01-26T14:05:39.153Z","2.8.1-canary.76.6952513b0f21d83df371e50d35d87e0260ff8bdf.0":"2021-01-26T14:07:08.947Z","2.8.0-canary.76.1c7ffb9c6b52f5c942fe3ec269e48de28ba5872b.0":"2021-01-27T07:21:02.716Z","2.8.0":"2021-01-27T07:24:23.191Z","2.9.0-canary.77.3ac7741230d224bdfb6a2e2e0c5152b09f7d9a1e.0":"2021-01-27T12:40:48.158Z","2.9.0-canary.77.a5d2cab213e3d0e51f9414659158c527c1f4ac15.0":"2021-01-27T13:10:51.145Z","2.9.0-canary.77.3d9e13e0b3feca4b6e409b8f1ea3731b3b13a494.0":"2021-01-28T07:29:50.185Z","2.9.0-canary.77.344e6c32e0927897556df893cdc3092721a22f4d.0":"2021-01-28T07:39:24.210Z","2.9.0":"2021-01-28T10:45:11.544Z","2.9.1-canary.78.055d91baed3d32f1de0e9070e72f39f56536c38e.0":"2021-01-29T07:41:16.936Z","2.9.1-canary.78.554376f5cd657df6ad718e8f75efc7b5ae8dbb72.0":"2021-01-29T08:05:36.201Z","2.9.1":"2021-01-29T08:19:30.369Z","2.10.0-canary.79.77c24af4d9fbcedca207a15506f8392b298f191a.0":"2021-01-29T13:24:43.862Z","2.10.0-canary.79.9eeab22bfb9e0c0c7e42e29bb5559ac44625f2c5.0":"2021-02-08T08:34:32.204Z","2.9.2-canary.83.4d2d73245d2244318efa37864b47325b19935288.0":"2021-02-15T10:02:28.523Z","2.9.2":"2021-02-15T10:56:17.805Z","2.9.3-canary.85.5b834c33be8cb79be2c54d122c40b78e601732a5.0":"2021-02-18T08:44:44.196Z","2.9.3-canary.85.e73dd38866298f779aeb4f3fa6179f075cefc6d5.0":"2021-02-18T09:13:06.987Z","2.9.3-canary.85.fd8141f9a6fdca257e4de6189d1259f2b5e65c66.0":"2021-02-18T09:34:48.381Z","2.9.3-canary.85.ec032406c7888a6384b571ef01e9e5f8ceb93fa0.0":"2021-02-18T09:47:07.264Z","2.9.3":"2021-02-18T10:34:07.963Z","2.10.0-canary.79.ebc13d1f0246a4ce4254f1517b50f8550c51f43f.0":"2021-02-18T11:42:48.153Z","2.10.0-canary.79.b2008989f468d625a8d79d67822aff95889867d5.0":"2021-02-18T11:51:39.751Z","2.10.0-canary.79.52bfe5a7f354c4a3c5b5953837274d003ede93db.0":"2021-02-18T11:53:48.030Z","2.9.4-canary.86.f67a998f06d936633336655947b52ad2afc6c772.0":"2021-02-19T07:23:59.468Z","2.9.4":"2021-02-19T07:36:40.734Z","2.10.0":"2021-02-24T07:48:09.494Z","2.11.0-canary.89.95e554f3fdb1110d7a9fc35e732dbf8e43983817.0":"2021-02-25T10:09:22.101Z","2.11.0-canary.89.0a9a6d3a666379dee88f3368a0760a43519468fa.0":"2021-03-01T08:56:37.108Z","2.11.0-canary.89.aaa6b1e2523efeca680efde8385eb75f2be13039.0":"2021-03-01T13:22:55.331Z","2.11.0-canary.89.b36417bbf872b55bafe58bc23bd197658d71566a.0":"2021-03-01T14:00:08.661Z","2.11.0-canary.89.729ca70d5e76fa1672b3cd21140459928aa61b24.0":"2021-03-01T14:01:16.434Z","2.11.0-canary.89.16d06df683f8290e083b5e34d2d69deb1a085428.0":"2021-03-01T14:02:35.178Z","2.10.1-canary.90.008409a59e9ef49eb964feb55c6207bc0bbd4d32.0":"2021-03-02T13:02:43.962Z","2.10.1-canary.91.cfc10bbe330aea82a07ea223c212af14fc582c5b.0":"2021-03-04T09:33:16.375Z","2.10.1":"2021-03-04T09:56:06.489Z","2.11.0-canary.89.cfacfb8f79e8a8f8849c1c9bef0b66e08135c331.0":"2021-03-10T10:52:34.597Z","2.10.2-canary.94.e2aba4c1fd45236de4a74d9290728f78790087b5.0":"2021-03-10T12:28:36.407Z","2.10.2-canary.95.edae68dab6bcc09110684fb6442efd93361c797b.0":"2021-03-11T10:11:22.940Z","2.10.2-canary.95.c4a4d28bef2a4aacf3a3669be9675e66f99582e5.0":"2021-03-11T10:33:28.349Z","2.10.2":"2021-03-12T12:23:44.024Z","2.11.0-canary.89.74a3bf7281113c9de2365bc5307ecc768e0f5167.0":"2021-03-12T13:06:45.656Z","2.10.3-canary.95.a827465636976d0d4bc61a8bd356048f4186b1cb.0":"2021-03-12T13:09:13.792Z","2.10.3":"2021-03-12T13:16:18.048Z","2.11.0":"2021-03-15T08:09:34.119Z","2.11.1-canary.96.c2d5e659995d90bbec3ad6edd1a43757f0b52931.0":"2021-03-15T17:06:55.500Z","2.11.1":"2021-03-15T17:21:46.559Z","2.11.2-canary.97.987759fc44c8f795e4220393b16666ce743e6af6.0":"2021-04-01T08:24:08.917Z","2.11.2":"2021-04-01T08:28:50.052Z","2.11.3-canary.98.d553dbb9a97243f506448692fd9a252f7ec76729.0":"2021-04-02T07:47:25.690Z","2.11.3-canary.98.92419efcaab792386427c3d98caec628738fe29a.0":"2021-04-02T07:48:25.098Z","2.11.3":"2021-04-02T07:50:23.414Z","2.12.0-canary.100.149d6cc8c6e8d0723fb89c639a3dffe1cb967380.0":"2021-04-02T11:18:31.286Z","2.11.4-canary.100.aa24d4618e2180205df142e5c61b3bcd7d97f92e.0":"2021-04-02T11:23:10.104Z","2.11.4-canary.100.2a5b040a3fafda6d1041a2287c3bf819d04c3387.0":"2021-04-02T11:26:49.556Z","2.11.4":"2021-04-02T11:27:36.665Z","2.11.5-canary.102.1bf6d5fe2795c516fabcfb2fcb2a6717f917dea4.0":"2021-04-02T12:29:48.779Z","2.11.5-canary.104.8ee09d5d6fe7a200745740bb1dba5fe7449f0edb.0":"2021-04-05T10:40:50.810Z","2.11.5-canary.104.ffe2f649cf45d6178a95459d479dd53d47676c8a.0":"2021-04-05T11:12:11.055Z","2.11.5":"2021-04-05T11:16:57.243Z","2.11.6-canary.105.48986d606ba17c128a7d05a26967b15117c875b7.0":"2021-04-07T11:20:33.763Z","2.11.6-canary.105.716a05dce76c52c9d55c6b817433a3fc6cca5fcf.0":"2021-04-07T12:51:45.480Z","2.11.6":"2021-04-07T13:30:35.036Z","2.11.7-canary.108.50a6054b8d581c0fb7dbe8c21cb193b4e8c7fdf0.0":"2021-04-12T12:58:35.274Z","2.11.7-canary.108.ab8f7553e8399e0904fd92c817e6f5a74d7edf16.0":"2021-04-13T08:38:58.358Z","2.11.7-canary.108.ca7e45a83cbfec1c1e7cafd3897761592d270f88.0":"2021-04-13T11:57:18.350Z","2.11.7--canary.109.8b91d7e65657bce0b2291d2dfa03a6d935f251ef.0":"2021-04-14T08:26:11.743Z","2.11.7--canary.109.6d633c21205286d91e9e2e16a77466a130c25b79.0":"2021-04-14T08:27:45.967Z","2.11.7":"2021-04-14T08:28:29.302Z","2.12.0--canary.110.ebfeaef35c5e7164b5c7c35cf32864c2ec1b21b1.0":"2021-04-14T12:23:22.603Z","2.11.8--canary.110.79495163f888e0e21ef7e72e49333a2bd1f18ace.0":"2021-04-14T12:30:14.968Z","2.11.8":"2021-04-14T12:33:40.127Z","2.11.9--canary.111.d28ec352e1f0d8c2d48ae191126675a2c804d9fe.0":"2021-04-15T09:18:07.254Z","2.11.9--canary.111.2975ed6a45ecfcd7ddfb0e27b7bb24f760bf3a39.0":"2021-04-16T06:48:29.270Z","2.11.9--canary.111.bf547e8401c7516035e045f3318d79e29cffb029.0":"2021-04-16T10:26:28.875Z","2.11.9":"2021-04-16T10:30:10.657Z","2.11.10--canary.113.0080e999871adc8c1244160a407de3317d5b2d41.0":"2021-04-20T13:09:15.217Z","2.11.10--canary.114.5b05ab435fd4da6fee0cc1c5a8975a0358d0c644.0":"2021-04-22T08:17:41.234Z","2.11.10--canary.114.0d1e8c597b17f3df446acb415a764368fcca8961.0":"2021-04-22T14:02:56.056Z","2.12.0--canary.114.040ea6ff05e0697ac3f5a66cbf9b8c7c440f3e3d.0":"2021-04-26T08:50:02.259Z","2.12.0":"2021-04-26T12:33:54.260Z","2.13.0--canary.115.1b76240d499e6bc912bf4897f2173a1aaa1bf9da.0":"2021-04-30T11:38:34.130Z","2.13.0--canary.115.23ac11b7c1d7cb3e555383e46556cf81f478266e.0":"2021-04-30T12:00:12.644Z","2.13.0--canary.116.0c5a14a619419f87151c9c8984b84bfb34d72182.0":"2021-04-30T12:27:11.140Z","2.13.0":"2021-04-30T12:33:22.824Z","2.14.0--canary.117.509a7a4768bfdb07d872c486d695bfc7663992db.0":"2021-05-05T13:43:28.501Z","2.14.0--canary.117.a8e3a60121bd5ff3cc6caf8ef79290d274e7e8d4.0":"2021-05-05T14:58:13.924Z","2.14.0--canary.117.8a57d4fa7420789efd3c8ad28a01e477d9734389.0":"2021-05-06T05:18:52.880Z","2.14.0":"2021-05-06T11:15:04.702Z","2.14.1--canary.118.7b31056ef207f2fe9a88fc885a913c004a7e211a.0":"2021-05-12T08:25:08.976Z","2.15.0--canary.119.2055ce7f5b0fdf706a3c7fddbda9e1cc5a4dc91b.0":"2021-05-12T11:11:47.900Z","2.14.1--canary.120.5fad138fe2baec0cdb96a2c6fcad3190222be449.0":"2021-05-12T12:07:15.136Z","2.14.1--canary.120.185465c785f17011ad97727603b981b861492854.0":"2021-05-13T06:59:29.568Z","2.14.1":"2021-05-13T07:09:06.108Z","2.15.0--canary.119.0da442699155b4b93da2387831321a5b5945d2c6.0":"2021-05-14T11:07:21.285Z","2.15.0--canary.119.d80f461cf3606671bcdc6bb450f559f407d15ccd.0":"2021-05-14T11:37:01.955Z","2.15.0--canary.119.2c78b051aea19898a82d89fb430223af41b27b72.0":"2021-05-14T11:45:32.791Z","2.15.0--canary.119.e4007f5cc6b9c28f78d3a19ed11f5ce0b484e8ac.0":"2021-05-14T11:51:19.864Z","2.15.0--canary.119.ed8a412a1fc4e9e86858de85493e748de55a8d3c.0":"2021-05-14T13:19:46.503Z","2.15.0--canary.119.f3b3681d89f682925d6b1d3d512eb60c5ad8df2e.0":"2021-05-15T04:43:03.046Z","2.15.0--canary.119.ea6a89ac161f9bbe8a4fc01e09d047a0b33cb5ea.0":"2021-05-17T06:01:10.932Z","2.14.2--canary.121.182f8dc43e81e6093c735822033f09354062dfb1.0":"2021-05-17T06:14:28.274Z","2.14.2--canary.121.fa69777f38ac8fcda22426f80e1ee691cbc22732.0":"2021-05-17T10:03:18.507Z","2.14.2":"2021-05-17T11:03:34.567Z","2.15.0--canary.119.a66f5caa41b24cf8a1b3f861c7bc39a35c1fd6cc.0":"2021-05-17T11:14:30.859Z","2.15.0--canary.119.546d344efee549489577d69798f1b8c0540dfe52.0":"2021-05-17T11:14:45.078Z","2.15.0--canary.119.c6dca236b41a73f6575248452015bbcf247b0a7c.0":"2021-05-17T13:57:06.630Z","2.15.0--canary.119.c41fd4b4e6f2be1cdd529aa6192f5787c5b46cd7.0":"2021-05-18T07:31:10.928Z","2.15.0--canary.119.6605900372ffba0d0a23aaf4ed0ec20327c4ea4e.0":"2021-05-18T09:13:43.750Z","2.15.0--canary.119.9b9690335c22948c1d5414042d892177942ce4bd.0":"2021-05-18T09:14:51.386Z","2.15.0--canary.119.a094b4ab5c092ce1c55915d1512bf14a1d7edbe3.0":"2021-05-18T10:45:39.793Z","2.15.0--canary.119.28ed78d731db598da92db11ef37c49a7f418c8cc.0":"2021-05-19T09:35:42.192Z","2.15.0--canary.119.1c4c2e35f4861700dc383473bdc5eb928a8da427.0":"2021-05-19T10:09:55.508Z","2.15.0":"2021-05-19T10:41:54.066Z","2.15.1--canary.123.1cea42c1ef72e9d85cb7c81eadcc51809f8d7dd6.0":"2021-05-19T12:58:34.492Z","2.15.1":"2021-05-19T14:12:23.264Z","2.15.2--canary.124.4bd9bbfe8a19b608c7d7b77e0a91dbbe691d5b71.0":"2021-05-20T18:55:44.994Z","2.15.2":"2021-05-21T05:36:06.633Z","2.15.3--canary.125.940a4b59206aa732963527d300025d8006175549.0":"2021-05-21T06:43:43.431Z","2.15.3--canary.125.c65caa0902d6469fbce4acdbb732bd46e410e5a9.0":"2021-05-21T06:56:37.969Z","2.15.3":"2021-05-21T07:08:23.604Z","2.15.4--canary.126.fe34455232d85703ede3aaba9a7aabf9b6f72877.0":"2021-05-21T16:02:20.334Z","2.15.4--canary.126.37f78c828f378be77f9af3adae78c9d0c888d67f.0":"2021-05-24T06:52:48.040Z","2.15.4":"2021-05-24T08:21:15.610Z","2.16.0--canary.127.fb4399e83519e8c004a3ddb6d50099d7a61e6a6f.0":"2021-05-24T10:07:20.967Z","2.16.0--canary.127.a9a87128b14f75817aa8ff3ab87e47e7a4b62a8c.0":"2021-05-24T10:36:15.891Z","2.16.0--canary.127.f9e3ea774ab5da30be6f877e447b66caa5f4bb67.0":"2021-05-24T10:48:31.034Z","2.16.0":"2021-05-24T10:54:42.180Z","2.16.1--canary.128.f1a19a9f078f48d7aaabab806c6193cc22afe52e.0":"2021-05-24T14:48:48.895Z","2.16.1":"2021-05-24T15:41:13.929Z","2.17.0--canary.129.dae0f0fb40ab1b7a1a501f7ced719985fc52d3dd.0":"2021-05-26T10:43:48.976Z","2.17.0--canary.129.ac9c09aff4a687979ca1b730c40ff5a36e8bed2d.0":"2021-06-02T09:25:29.601Z","2.17.0--canary.129.7a4ad6e6912326ce24a9b34abe5284e578baa7a7.0":"2021-06-02T09:55:54.878Z","2.17.0--canary.129.0b36f7510a2707a4b3d55f5b453d2f4957ce69e6.0":"2021-06-03T11:41:51.821Z","2.17.0--canary.129.4eeb7fa28f3d15f823cfec7b49cee1a59c220176.0":"2021-06-03T12:51:05.621Z","2.16.2--canary.132.22ae2dbfbf62648c00068744e3006eca33e8959c.0":"2021-06-05T06:51:17.958Z","3.0.0--canary.129.dff7486040835b0ed9349c25ca905b0c5c5ce55e.0":"2021-06-07T11:48:17.239Z","3.0.0--canary.129.b493b78020a5a98d998be32a91184037d3dbaccd.0":"2021-06-07T13:35:42.336Z","3.0.0":"2021-06-07T14:21:32.978Z","3.0.1--canary.132.a7f81cc1cb8b8e953734b661934d13f807c37ddc.0":"2021-06-07T15:35:47.719Z","3.0.1--canary.133.9872db671e7586f46d55ec048488eeda780c9b17.0":"2021-06-08T10:47:58.019Z","3.1.0--canary.134.924b11e784307d463503e4e2b41f79eceb6b4396.0":"2021-06-08T11:52:32.075Z","3.0.1":"2021-06-08T12:47:12.032Z","3.1.0":"2021-06-09T07:54:08.850Z","3.1.1--canary.135.ea4d71e2036f9323fb24bfdabcdf2807ffd9bb25.0":"2021-06-09T08:50:29.052Z","3.1.1":"2021-06-09T09:13:39.566Z","3.1.2--canary.137.317a672ea55ca3ae4aaa66547ba736c55cd938fb.0":"2021-06-15T07:57:17.884Z","3.1.2--canary.137.d0dbff8b7a5ad6a66ef66c4568f90fee0cbf8786.0":"2021-06-16T06:08:10.974Z","3.1.2":"2021-06-16T10:39:53.771Z","3.1.3--canary.138.aad571b58e8495f76799803193098be65d462e43.0":"2021-06-16T13:34:47.336Z","3.1.3--canary.139.070743011deb5c9600ad3f868be4fd194bb2db87.0":"2021-06-17T13:12:13.357Z","3.1.3":"2021-06-17T14:10:34.245Z","3.1.4--canary.140.7c04943b0511ffa0804719742e50aefd25329b70.0":"2021-06-18T07:35:07.260Z","3.1.4--canary.140.7281c0c6b16e21f2bd751f8d129b2639b547d3ec.0":"2021-06-18T10:27:55.224Z","3.1.4--canary.141.5c48c6172ddba2e94443dcf52658fddc63ec6227.0":"2021-06-21T15:29:48.104Z","3.2.0--canary.142.a8dcf88c19e645dfaf8fc1544c5b3d158a90bd6f.0":"2021-06-22T07:36:11.343Z","3.1.4--canary.141.c52369d38226c90a17fc3782b603b79ff53abc37.0":"2021-06-22T12:59:04.948Z","3.2.0--canary.142.7fc1eeb2d8fb25c988634172b82eec30d2599369.0":"2021-06-22T13:58:21.207Z","3.2.0--canary.141.8f240a5806408156ff3dd95962684bf8345786ab.0":"2021-06-22T15:29:34.281Z","3.2.0--canary.142.0b6a469909055cf4ebb25e80a11f93b5d335bea7.0":"2021-06-23T07:32:34.205Z","3.2.0--canary.141.0bfa854d335eb9cb2305c9311bdcc1bfcc43357d.0":"2021-06-23T08:17:12.498Z","3.2.0":"2021-06-23T09:15:59.246Z","3.2.1--canary.141.35b2c78ea672c7b6b9f7a1a33c77e92a1a360ca4.0":"2021-06-23T11:22:54.146Z","3.2.1--canary.141.8b64ad708755ce8779179a5fa7353d6a92abfa2d.0":"2021-06-23T13:52:17.427Z","3.2.1--canary.141.18b6054c94bed98ec2758f0446a60e3b47ad3c1c.0":"2021-06-23T13:54:17.681Z","3.2.1--canary.141.f7e643a47ac4546d75bc73138de272dc7128f603.0":"2021-06-23T14:07:20.375Z","3.2.1--canary.141.a4a52f70508462777c549db3db4c32a674a44900.0":"2021-06-23T14:58:54.222Z","3.2.1":"2021-06-24T10:00:56.953Z","3.2.2--canary.143.da5ebe43d4912beae154c00674da7c009f9a3345.0":"2021-06-29T08:23:10.993Z","3.2.2":"2021-06-29T10:22:28.221Z","3.2.3--canary.145.19cbdddccd6f47d20e697d0051ad5d2443c3c1af.0":"2021-06-30T10:36:43.856Z","3.2.3--canary.145.59cec087ecf1b9908d32a19b05e495fc18b0c614.0":"2021-06-30T13:23:31.459Z","3.3.0--canary.146.9c210de4c5edcf9736b993fc04fd7887cdb95ed4.0":"2021-07-01T10:03:58.274Z","3.3.0":"2021-07-02T08:19:59.531Z","3.3.1--canary.145.6472bd7d5981057b6015745f12a9cf942f58499a.0":"2021-07-02T10:53:24.774Z","3.3.1":"2021-07-02T10:57:03.050Z","3.3.2--canary.147.46ae3987fab8944f98c84398fd848f148bb61d73.0":"2021-07-05T09:30:10.035Z","3.3.2--canary.147.baed5e92bd48f7ac0a9dcf71a58d72019d59b038.0":"2021-07-06T09:15:32.604Z","3.3.2--canary.150.accfb890e03133b6b51b97a9e81bacccbf2a4e48.0":"2021-07-07T11:05:25.191Z","3.3.2--canary.150.4ff9e0d98ca84cb8504e29b188d9419317fc1c63.0":"2021-07-07T11:09:27.626Z","3.3.2--canary.150.9fc6423739d5691a5593b48a46487df8a19b3d0f.0":"2021-07-07T13:10:17.989Z","3.3.2--canary.150.ef085824d1f72a8bf71f9a7a161c9a24ed405b9b.0":"2021-07-07T15:19:31.406Z","3.3.2--canary.147.cd171daca04dfa6a21329e031950dc28326ef780.0":"2021-07-08T07:58:17.490Z","3.3.2--canary.150.97740c88f77d62d9c16e886cde62b25d69973eef.0":"2021-07-08T08:10:36.356Z","3.3.2--canary.150.4f47f4fed553266b572e4094bf6bf956ddc737ff.0":"2021-07-08T08:31:48.386Z","3.3.2--canary.151.6c14f250785aef545f784211755ba6390f01e2af.0":"2021-07-08T12:12:04.683Z","3.3.2":"2021-07-08T12:17:30.469Z","3.3.3":"2021-07-09T04:18:43.334Z","3.4.0--canary.152.b82a162fc4f75463d6fd350be0150f83f4eb99b7.0":"2021-07-09T08:12:53.958Z","3.4.0":"2021-07-12T06:59:43.231Z","3.4.1--canary.154.33321e71609b360e4bcce573f444b249367082a6.0":"2021-07-12T12:32:29.189Z","3.4.1":"2021-07-12T12:41:32.310Z","3.4.2--canary.155.4bb896416936db16f275cf9145f98bd8056f4130.0":"2021-07-13T08:42:12.535Z","3.4.2--canary.155.a756767b28b4b7822ec4c25d33c1651d4eac1b10.0":"2021-07-13T08:45:01.464Z","3.4.2--canary.155.8086cc0b6644203419275a7e525a539e49575f86.0":"2021-07-13T08:46:42.190Z","3.4.2--canary.155.18b67de8e49be098b99dd7b01ef307ce0a961db7.0":"2021-07-13T08:47:01.180Z","3.4.2--canary.155.a15369ff82e8a7d3aa646dc730e09909bf38ddc3.0":"2021-07-13T11:10:27.699Z","3.4.2":"2021-07-14T08:18:50.668Z","3.4.3--canary.156.99808f9e304abc4eadac9869de16d525cd6b00f5.0":"2021-07-14T11:59:09.589Z","3.4.3--canary.156.ef01cd284c20fc93dd9993d63015133db1007c57.0":"2021-07-16T09:23:36.843Z","3.4.3":"2021-07-16T09:47:52.747Z","3.5.0--canary.157.a82e6b66f6f10bfc6c7d56217e381f20f2e3d62b.0":"2021-07-19T08:58:31.401Z","3.5.0--canary.158.df51ac9198a96599eecfe7cc10985034eeaceb2f.0":"2021-07-19T10:01:30.497Z","3.5.0":"2021-07-19T10:08:29.617Z","3.6.0":"2021-07-19T10:21:56.992Z","3.7.0":"2021-07-19T12:19:03.726Z","3.7.1--canary.159.c35f7fcb93895cfe42c81808341810bf25382fb2.0":"2021-07-19T13:52:44.055Z","3.7.1":"2021-07-20T11:57:50.410Z","3.7.2--canary.159.1dcfd0d11c18b5f2035642e6e5fb8248a784c072.0":"2021-07-20T13:01:31.154Z","3.7.2--canary.159.51d9bd76c132db619ddf3992bfcaa390cfe8dd8e.0":"2021-07-20T13:03:19.174Z","3.7.2--canary.159.0b552ec72135db8d05aab5673861c448c37b67e7.0":"2021-07-20T15:19:19.058Z","3.7.2--canary.159.36ac6f6750110a3e1b23b195067055483af17d2d.0":"2021-07-21T13:28:30.050Z","3.7.2--canary.159.c437c9a6861497cc1adb77c1ff7a071614f7d974.0":"2021-07-21T13:44:59.340Z","3.7.2--canary.159.37ed9c068bda10b90e00f745b55b62b8fd0e46a9.0":"2021-07-21T13:56:50.114Z","3.7.2--canary.159.0f3bf007a57e3be543a5e3862a170c434367ea6f.0":"2021-07-22T07:25:13.369Z","3.7.2--canary.159.3a997572dd009b435aec9f63b7fa85330761d6e4.0":"2021-07-22T07:26:50.888Z","3.7.2":"2021-07-22T09:30:30.910Z","3.7.3--canary.160.bab31c8ff3fa1115816c96991662909f8ee551a5.0":"2021-07-22T11:53:08.387Z","3.7.3--canary.160.03a1d2d4deedd9a6c67886f55a7dbb408995aea7.0":"2021-07-22T12:24:40.464Z","3.7.3":"2021-07-22T12:44:27.182Z","3.7.4--canary.161.a406425a0f940689503b1ba8da62c1af6f5241ad.0":"2021-07-26T08:45:08.274Z","3.7.4--canary.161.fefed6f18241c5a30d17cb07784677406d52276f.0":"2021-07-26T08:46:14.015Z","3.7.4--canary.163.6b1e802453e56533a4da3abe6cd05828f53b22c6.0":"2021-07-27T12:43:34.176Z","3.7.4--canary.163.d6c54bf84d87726fd220abb50e382ef3777ae707.0":"2021-07-27T13:21:54.556Z","3.7.4--canary.163.97727bbf6a427f7ca1d6e1f2bbe313a261e592e7.0":"2021-07-27T13:28:28.909Z","3.7.4--canary.163.07419654235526ecf398d0d348ecf4e42c2412f9.0":"2021-07-27T13:41:55.088Z","3.7.4--canary.163.20f1414cceb19c771fb19fcb764c3551c80f7e87.0":"2021-07-28T07:07:59.541Z","3.7.4":"2021-07-28T10:50:37.194Z","3.8.0--canary.164.5ff78a99ab4c79caa3ed2fa467e9f2ceadbed313.0":"2021-07-29T12:36:29.752Z","3.8.0--canary.164.2567be318fe6cc6022674c270fff8aeffb8c3503.0":"2021-07-29T12:51:26.254Z","3.7.5--canary.166.0ee1dcfdfbc9353a1a7bf3c934ea0f6ee96ddbc4.0":"2021-08-04T06:55:01.751Z","3.8.0":"2021-08-04T08:43:14.445Z","3.8.1--canary.166.52fc3152eec35b364b5cc01a3ebf7ea1ab76f05a.0":"2021-08-04T10:32:20.911Z","3.8.1":"2021-08-04T12:28:55.596Z","3.9.0--canary.168.c54879dd63597e6c503969197d21bd21e4699e7f.0":"2021-08-05T08:01:45.234Z","3.9.0--canary.170.3944ff197944f47df447daecfc345f4067c228f7.0":"2021-08-05T13:04:44.096Z","3.8.2--canary.171.e6ef194c9165612960bddfb0dbe8cf0fe731e718.0":"2021-08-05T13:55:54.192Z","3.8.2":"2021-08-05T14:54:56.734Z","3.9.0--canary.168.0a377c24909944c824cd2cc2f8c5c225b2748963.0":"2021-08-05T14:58:13.426Z","3.9.0":"2021-08-06T08:44:54.592Z","3.9.1--canary.172.02e84b64a9d59ddfc659eae1a2e25a6fb379eed9.0":"2021-08-09T09:26:19.528Z","3.9.1--canary.172.fc0512a11e792921fe1a33d0002fa81f85dcb36f.0":"2021-08-09T11:05:23.337Z","3.10.0--canary.168.b10cee3c4ba2b81251653f0dd3a49fce7ef6f6ad.0":"2021-08-09T13:20:08.797Z","3.10.0--canary.168.91626e9ecf78e6ae7e2e15ea8032e23ce99abb7d.0":"2021-08-10T07:51:28.308Z","3.9.1--canary.168.0e45c6c949e6557c436430f5ad2e26b418ebaf9d.0":"2021-08-10T08:26:22.974Z","4.0.0--canary.168.1a348e8a90e19edd9fd8ee7d223e8f65ef9b7d6c.0":"2021-08-10T08:31:22.158Z","4.0.0--canary.168.99d7671e8dfaa4602d1708c2a320a3b40cbf90d2.0":"2021-08-10T09:11:29.126Z","4.0.0":"2021-08-10T09:19:26.280Z","4.0.1--canary.172.839903d45b274252648f0479a4c494e3a3e5d83f.0":"2021-08-11T07:44:43.822Z","4.0.1":"2021-08-12T08:30:39.281Z","4.1.0--canary.173.2ea6edfe4e3d2e8c67ae4101df808ab4b1b23bb5.0":"2021-08-16T15:52:30.946Z","4.0.2--canary.175.ea34ac813a11a2362c5f826d20ebfca166e96ca9.0":"2021-08-17T15:54:58.918Z","4.0.2":"2021-08-18T08:41:50.528Z","4.0.3--canary.176.d5a7c3b828c3c7ba08d2e9913bb51a237c167944.0":"2021-08-18T16:17:06.321Z","4.0.3--canary.176.89f28f85e25f83f0db50456ab6999eadf15e4d4f.0":"2021-08-19T11:23:38.525Z","4.0.3--canary.176.035d5dcbd98c3128e2567567f23c4a60fefdac89.0":"2021-08-19T12:26:02.293Z","4.0.3--canary.176.7585c299cff99b882988bc3c2b555128d26c010d.0":"2021-08-19T12:27:04.336Z","4.0.3--canary.178.b0b950cdb22f9caf09649fb730d27d218a31e603.0":"2021-08-19T12:49:35.427Z","4.0.3":"2021-08-19T12:51:28.144Z","4.1.0--canary.173.8b21706c5ae9a47574fee469a0d72241e0324d10.0":"2021-08-19T12:54:13.309Z","4.0.4":"2021-08-19T13:52:14.466Z","4.1.0--canary.180.edc2faa1414cc2fd516d758801856c7ac79c1ded.0":"2021-08-23T07:07:16.505Z","4.1.0--canary.180.e787923bd251044ab78df0620567ac40ab779a44.0":"2021-08-23T07:43:29.623Z","4.1.0":"2021-08-23T07:47:56.824Z","4.2.0--canary.173.cbd1319fc895ce0dc1a2067cdefaa19209ef35a8.0":"2021-08-24T09:24:52.653Z","4.2.0":"2021-08-24T12:54:51.115Z","4.3.0":"2021-08-25T10:45:10.418Z","4.4.0--canary.182.cf66a112b36dab90ab2433d7f41b7dfec0dd4cbc.0":"2021-08-26T07:42:14.274Z","4.4.0--canary.182.21cffc4226831c7ac7a1163aba1cdfbe390211d4.0":"2021-08-26T07:45:49.331Z","4.4.0--canary.182.e36c10312312d562c41e5c89fb469bb515a13a8a.0":"2021-08-26T07:46:50.962Z","4.3.1--canary.184.09debcd03641afeba404b8c6deb99ba2d1a43694.0":"2021-08-31T10:56:22.422Z","4.3.1--canary.184.17094815990a302a26563229398dbee0a7530701.0":"2021-08-31T11:51:48.685Z","4.3.1":"2021-08-31T11:57:01.077Z","4.3.2--canary.185.04491ddf44edd6a34c30fee7929ad695528241dc.0":"2021-09-01T08:34:56.213Z","4.4.0--canary.186.5f743be0179c80a5a71d35e1f667760df5aac22d.0":"2021-09-03T10:01:32.151Z","4.3.2--canary.188.e6d89c365c618abdd8cdbadc17ca798d30220e09.0":"2021-09-07T08:09:25.012Z","4.4.0--canary.189.81b4df9a8673dbbdd73d8e65766a64d4fd575789.0":"2021-09-07T13:06:39.476Z","4.4.0--canary.186.2ab5d7d316a5140309b676be55e935ca8b306853.0":"2021-09-20T12:15:14.762Z","4.3.2--canary.192.b200bcc410a04876f0fd5b091e4e8feb4300951b.0":"2021-09-21T09:39:02.214Z","4.3.2--canary.193.b7196bff35292da813f7f7b1afb24de4173edd61.0":"2021-09-21T09:42:47.838Z","4.3.2--canary.193.c24a5ed99224719f5a27a3479153e498711c53fe.0":"2021-09-21T12:36:53.874Z","4.4.0--canary.195.dfbec69d562d8f506ebcfc17c10840eb27df41a8.0":"2021-09-21T15:14:37.900Z","4.4.0--canary.195.3a667264893389b853b70286632c53c4eac5b11b.0":"2021-09-22T07:12:50.231Z","4.4.0":"2021-09-22T07:37:37.608Z","4.5.0":"2021-09-22T08:05:28.739Z","4.6.0--canary.196.c65771fe516f95f32eb9d752d7c464f190b2ec15.0":"2021-09-22T08:42:42.006Z","4.5.1--canary.193.890a9bb05bac8ae1ccee50e96eaafa3b68e048e0.0":"2021-09-22T09:59:01.951Z","4.5.1--canary.185.8981adcb9163fa2d9b15c817bd1447538db313ed.0":"2021-09-22T12:37:32.369Z","4.5.1--canary.193.c5303589550197df47578ce75ff49fd58332a83e.0":"2021-09-22T16:45:06.027Z","4.6.0--canary.196.056d4062ccef39e511acf5ab5ed7f888f313dfd2.0":"2021-09-23T06:50:37.508Z","4.5.1--canary.199.9ef1cfcb223d844a519462993d14c1b05ff78123.0":"2021-09-24T07:59:46.657Z","4.6.0--canary.200.74308e16c65dc3feb715732ad06dc277e4f4fb5b.0":"2021-09-27T06:50:26.594Z","4.5.1":"2021-09-28T13:30:55.658Z","4.5.2--canary.201.2e53cd28fded271007bb09fd7f88c3246186b7c4.0":"2021-09-29T10:06:33.487Z","4.6.0--canary.202.38cc3d5a83baa8e1008463c218e63b0a3461ab7e.0":"2021-09-29T12:53:52.543Z","4.6.0":"2021-09-29T13:05:17.935Z","4.6.1--canary.203.6d9b8b9274c65863d60fa88f14ef9aa84fc45052.0":"2021-10-01T13:09:26.307Z","4.6.1--canary.204.41bd367f728e700d45b989943a044800197ed2b8.0":"2021-10-01T13:32:20.014Z","4.6.1--canary.205.5a23353f3ffe085e45dc376150729d35bd5fea0a.0":"2021-10-01T14:50:27.734Z","4.7.0--canary.206.d0876e7b78199f7cd5317975b1087739ca509734.0":"2021-10-03T08:00:46.093Z","4.7.0--canary.206.705a143b8d70ab82d5d541e0c63383b462b9322d.0":"2021-10-04T07:06:29.569Z","4.6.1--canary.205.918ebfb9e3d019a822978bb74beb4aa8f4da501a.0":"2021-10-04T07:21:06.704Z","4.7.0--canary.206.c30308795a20682c6fbfd7ae5ff119e4b5d6e43b.0":"2021-10-04T12:17:17.105Z","4.7.0--canary.206.0472eb24dfb84478c9f50a75cfc98305ef20f3c3.0":"2021-10-04T12:43:50.653Z","4.7.0--canary.206.f6f49b43ef27770b658cc770f302eb433328e057.0":"2021-10-04T13:40:29.524Z","4.7.0--canary.206.e541c53da8f3b6eabd1daf61b30860c260b5e192.0":"2021-10-04T14:18:02.908Z","4.6.1--canary.205.81d2495781acff0ae9388d5f4df4a2860a572315.0":"2021-10-05T12:20:35.338Z","4.6.1--canary.205.6e1333e9d058593ac584d2bc13f8d2ca8b26f674.0":"2021-10-05T13:32:02.790Z","4.6.1--canary.207.e082d127b3bb9b562010f6aeebee9c38975f1e86.0":"2021-10-06T10:33:12.018Z","4.6.1--canary.207.5a32d96167f7b9f7986a85ae134120c9ed87c4d9.0":"2021-10-06T11:09:21.729Z","4.6.1--canary.205.a90a005023f5073188d0adff2d9abed543ec62a3.0":"2021-10-06T11:13:32.345Z","4.7.0--canary.207.82690778300022c0cdb676df11b1d1da6d1df74b.0":"2021-10-06T11:49:11.846Z","4.7.0":"2021-10-06T11:56:37.073Z","4.7.1--canary.208.163760deba52855c338f91126fda1ad1254f8d1c.0":"2021-10-06T12:02:18.197Z","4.7.1--canary.210.9250d17451adb9dc7e7b53d5f99911795bd410ae.0":"2021-10-11T07:19:48.213Z","4.7.1--canary.211.b2ea6c7e024ed5ce743d5408e216bfa92f19f0ec.0":"2021-10-11T07:24:58.363Z","4.8.0":"2021-10-11T09:25:27.650Z","4.8.1":"2021-10-12T07:38:30.896Z","4.8.2--canary.212.796835ea7f38be55cfa2c56901fa0eebfc46cf5c.0":"2021-10-12T09:26:40.934Z","4.9.0--canary.202.e1e53a4cd90d8edc943f3715c98a065da909022d.0":"2021-10-12T10:29:35.710Z","4.9.0":"2021-10-12T10:38:01.783Z","4.10.0--canary.216.a08c891c30d2cd28d69c9d8b69b82c26de123396.0":"2021-10-20T09:56:09.720Z","4.10.0--canary.217.d1ac12c011343d9a67483738950dd97f0200a7ac.0":"2021-10-20T15:03:23.463Z","4.10.0--canary.206.f6ce665e94aab117ee01c4f139f96f0d5bff6480.0":"2021-10-20T19:15:27.023Z","4.10.0--canary.206.260462766c65a22f0ea012926a2c4e1ffb1eed87.0":"2021-10-20T19:23:19.922Z","4.10.0--canary.206.4290ab417b696464b9c8bdc2981d42091e825084.0":"2021-10-20T19:33:34.546Z","4.10.0--canary.206.05e7a08a1e239cb42c800205495d59400ca3d6d3.0":"2021-10-22T12:58:27.021Z","4.10.0--canary.206.fe1c6fab93d7046b07e8f154b2182a9a62487202.0":"2021-10-22T13:44:44.750Z","4.9.1--canary.222.b3dfbc353478f6f78b00490cfd9970e1ade05335.0":"2021-10-26T13:30:48.507Z","4.9.1--canary.222.0f487b33bba4096191af3e481c7bb5f640254012.0":"2021-10-26T14:03:21.718Z","4.10.0--canary.217.b977079c87795d648bd138aabf3191d95fa88d17.0":"2021-10-26T14:10:52.056Z","4.9.1":"2021-10-27T04:58:58.242Z","4.9.2--canary.224.f671bccecff603e9cdcc4b8a7df83ba02d6d8b2f.0":"2021-10-27T13:20:34.950Z","4.9.2--canary.226.84efacaada01b74d923de2ae8af5dec21b5ed849.0":"2021-10-29T08:25:58.965Z","4.9.2--canary.226.3f1a618ea2be182609a728c992a905b8553a7bff.0":"2021-10-29T09:13:49.262Z","4.9.2--canary.224.c62514bbeb9dc17a4a4e5908e41921379ce81d7e.0":"2021-10-29T12:04:07.231Z","4.9.2--canary.226.d3dd44f0598fdae10d3381cc54a461d2af2c46e4.0":"2021-11-01T15:59:36.032Z","4.9.2--canary.226.3b1982c6b259075dfb9f7b09528f0d64fa5ca7af.0":"2021-11-01T16:03:22.805Z","4.9.2--canary.224.ca2e3a9eac3852e15c64840f399cec9d8c7f87e2.0":"2021-11-02T13:14:59.681Z","4.10.0--canary.228.422b880d8b89cae6b302862e844b66f6f89cdb0a.0":"2021-11-08T14:31:46.935Z","4.10.0--canary.228.4b6c5c9baa715de142aea8ffb0d1e7cb3b358ae6.0":"2021-11-08T14:34:13.568Z","4.9.2--canary.226.002a2d544402d6ba3c5f2c5fd9fae45882061e0a.0":"2021-11-09T06:05:19.795Z","4.9.2--canary.226.2d49921cd786c5e5606c4d7e5129cac744d58d0f.0":"2021-11-09T06:09:33.563Z","4.10.0--canary.217.0e0c9ad00853fee1d64553202559e6d6ae837480.0":"2021-11-09T07:34:41.736Z","4.10.0--canary.217.61c59d5c905a6880310bdb4dc3ea35f85766a342.0":"2021-11-09T07:36:14.891Z","4.10.0":"2021-11-09T12:43:17.941Z","4.10.1--canary.230.a5ab34c4c394213c695b690897e09d6338bb2108.0":"2021-11-10T08:44:55.619Z","4.11.0--canary.206.c2b117a188bec5503599d965deabf9c0ee264b4e.0":"2021-11-10T09:27:56.790Z","4.11.0--canary.206.81c54b3e32797ed3b0e1661d80a8988fea632559.0":"2021-11-11T07:04:01.329Z","4.11.0--canary.231.1013eb9ad503f2fff3bf8ebb68f015b75f31a470.0":"2021-11-22T08:00:31.426Z","4.11.0--canary.231.4107d8ff03c4bc9b71ae9aa1f213ddf599757246.0":"2021-11-22T13:36:12.031Z","4.11.0--canary.231.98b00847a140e7b375fb91b96f9f107b251720a9.0":"2021-11-23T08:23:52.654Z","4.11.0--canary.231.696dc78beec5f59d5229d3d11d037591775046f1.0":"2021-11-23T08:24:56.787Z","4.11.0--canary.231.6af9b36d8a56c338038dcadaf0c7cceb6d1f315d.0":"2021-11-23T10:27:00.033Z","4.10.1--canary.233.0ca3f2bd9effb360e200d88bfbbd3c292c54c402.0":"2021-11-23T12:06:41.016Z","4.10.1":"2021-11-23T13:10:51.125Z","4.11.0--canary.231.85501fd38681cd14b29cc2b69c9567621e826555.0":"2021-11-23T15:53:15.253Z","4.10.2--canary.236.1e2a2d6f0619fc2f49d3b82809a72b7f0e7639d8.0":"2021-11-24T11:07:08.457Z","4.11.0--canary.206.65f6b8085419a12ba42dc73ca3afa33063b23db0.0":"2021-11-26T14:13:02.792Z","4.11.0--canary.231.7930f764fdb84e2fead72fe15df537272edcbe97.0":"2021-11-29T15:05:38.644Z","4.11.0--canary.231.0320eb30e108780a91f5f91824eb0e2e8111762d.0":"2021-11-29T15:09:45.174Z","4.11.0--canary.238.e87db38cac5a81f60f079fd459be79f9b99a6145.0":"2021-11-30T07:33:50.496Z","4.11.0--canary.231.f76657ef84ea8af1cc9cc2aa02f1463a9ffc538a.0":"2021-12-06T20:23:35.653Z","4.10.2--canary.238.308aeed034f9f168a331cf78408e2838e1667ce5.0":"2021-12-07T08:34:40.761Z","4.11.0":"2021-12-07T08:58:44.545Z","4.11.1":"2021-12-08T14:06:58.186Z","4.11.2--canary.239.a04e95f7b1864ffb4b1fda313042e452939e2aed.0":"2021-12-10T10:04:30.928Z","4.11.2--canary.242.3cddf26ae59d110bce1598e34dcc1956e66318f9.0":"2021-12-12T17:33:16.642Z","4.12.0--canary.243.6e737302b7f39b76dd39d10498c024cab9fec055.0":"2021-12-12T17:35:40.112Z","4.11.2--canary.243.0557b298f716b4cd69932f5055522aeab38e52a4.0":"2021-12-12T17:41:36.426Z","4.11.2--canary.246.2fe096b114c34902562a52219957cb8400036448.0":"2021-12-17T06:51:35.894Z","4.11.2--canary.246.a453dc19ec9778a2e35779b65f37ffc7f85ddc97.0":"2021-12-17T06:57:49.096Z","4.11.2--canary.246.98d032e125bd92a3413082d25a0b6e7639ea386c.0":"2021-12-17T06:59:18.803Z","4.11.2":"2021-12-21T08:43:26.586Z","4.12.0":"2021-12-21T09:26:10.141Z","4.13.0--canary.216.9767b59a5ddf38e70185bd566b7062ff88ce8f1c.0":"2021-12-21T09:29:57.709Z","4.12.1--canary.242.f3da956d84efd7c9a9121f3c8fc2a3be44cd4943.0":"2021-12-21T09:32:41.200Z","4.13.0":"2021-12-21T09:33:43.702Z","4.13.1":"2021-12-21T09:56:41.737Z","4.14.0--canary.248.7868ebf44498210539f2c9dbd8e3d01bfd821aa2.0":"2021-12-23T07:18:19.215Z","4.14.0":"2021-12-23T08:11:41.738Z","4.14.1--canary.249.e82cf1710f2fc81f7a96ec6611fc39ed0295246b.0":"2022-01-10T08:33:20.532Z","4.14.1--canary.250.bfb9b464275221f9b5d0e158358c7c6486151fe4.0":"2022-01-10T11:24:52.576Z","4.14.1--canary.249.400c5f2809b2cf3d93dd11f76be47425146ca6c9.0":"2022-01-10T11:51:33.963Z","4.14.1":"2022-01-10T12:02:59.599Z","4.15.0--canary.251.cb8d48a2fd642ce383c930d67c62e8422123973c.0":"2022-01-10T12:57:17.192Z","4.15.0--canary.252.dd7ce278844629b3f2938009bf5445e2af3ba565.0":"2022-01-17T14:31:05.088Z","4.15.0":"2022-01-17T15:00:44.885Z","4.15.1--canary.253.5e2f984bf9a73863e36a7090fb09500ae7728e1b.0":"2022-01-18T13:14:53.198Z","4.16.0--canary.254.849ed59a49164b3601aad9aa4bde933988877031.0":"2022-01-20T11:23:06.491Z","4.15.1--canary.255.a27267813272811dd31797b6a0dfdbb1d2f52bf3.0":"2022-01-20T11:39:11.856Z","4.16.0":"2022-01-20T11:39:54.244Z","4.17.0--canary.256.004c305de8dd0429be86cdeb3b7d18e7a7e5457e.0":"2022-01-25T10:39:30.601Z","4.16.1--canary.257.f9cea8ce416d0871507b4c97eb971781a2531a5f.0":"2022-01-27T08:25:31.180Z","4.17.0--canary.254.e45e4c34ae67bf4d0b3e4add1a826a1f83cc908f.0":"2022-01-28T12:31:50.406Z","4.17.0--canary.254.1255e8d9f29f84606916c721f8af87147249f87c.0":"2022-01-28T12:49:16.531Z","4.17.0--canary.254.a9ab08a9c7236f0aa8e7f38e5ad4f78d3714afd4.0":"2022-01-28T13:05:22.616Z","4.17.0":"2022-01-28T13:08:31.801Z","4.17.1--canary.258.3ef2f5051025f76acc3a499189c16cfef5f29631.0":"2022-02-01T08:36:31.011Z","4.17.1":"2022-02-01T12:39:05.644Z","4.17.2--canary.259.0ac7aca64257c5abc68213a662b050666c0eb68e.0":"2022-02-02T08:55:30.253Z","4.18.0--canary.260.5c34b40d4589f9f06e874b4faf485b95f47fef9c.0":"2022-02-03T18:25:37.270Z","4.17.2--canary.262.d6f9696e85d65b851d7329c59b84e402b0c223fd.0":"2022-02-04T12:24:02.305Z","4.17.2--canary.263.4109985eeb2ba9476ba3167c245dd14558586a5b.0":"2022-02-07T12:16:30.650Z","4.17.2--canary.263.c5e13fa2a6240c51c1c764a54b120b4b6f3a16d7.0":"2022-02-07T16:17:57.238Z","4.17.2--canary.265.b706e33b583084fd7abeb434dd8d1c5595c2224a.0":"2022-02-08T11:11:26.244Z","4.17.2":"2022-02-08T11:38:03.003Z","4.17.3--canary.268.f78592c0dd23bae6234ff974ad8f1c44939a5801.0":"2022-02-11T12:17:13.261Z","4.18.0--canary.269.2fa7a84e7a8721b69deab36944fe5e856ffeffeb.0":"2022-02-17T15:33:13.648Z","4.18.0--canary.270.a4ae2b45558c80d1261020210a0f21aee4ebe293.0":"2022-02-18T12:12:08.311Z","4.18.0--canary.270.8b271a3b378121826b9ec8c4114accae66bf8858.0":"2022-02-21T08:14:27.323Z","4.18.0--canary.270.05c822724ef7ef295b0f591116777b8da9dae6c4.0":"2022-02-22T14:03:48.756Z","4.18.0--canary.272.1b0fc82fd1b7921c6d8fc238f9d86cd3bd7230af.0":"2022-03-05T08:32:11.040Z","4.18.0--canary.270.ae005bd33c1cd9c8fdbbb8346eac96cfde98b1ac.0":"2022-03-05T14:45:24.185Z","4.18.0--canary.270.715d2d3340172fc7abe84de657abca02755c7eaf.0":"2022-03-05T15:07:47.667Z","4.18.0--canary.260.4c2c762e7827a9a32ecfa91476e7139ab8a2a719.0":"2022-03-05T19:05:55.448Z","4.18.0--canary.260.f667de7326ede5f30d85fce9f01a6afe46b2a5ff.0":"2022-03-05T19:06:55.599Z","4.18.0--canary.272.a5e3f743eaf476fdfa94805f4537ba168de07383.0":"2022-03-10T13:36:57.801Z","4.18.0--canary.272.f748c7ce92fa979965f272aa85a31ef70da3d4ef.0":"2022-03-11T10:48:39.045Z","4.18.0":"2022-03-11T10:54:03.080Z","4.19.0--canary.260.b76e5c015ecbe4a30573e00afcbbe983e2e209ec.0":"2022-03-11T13:31:11.805Z","4.19.0--canary.260.d4cacc7f74843d704f70a1e08b370810be8fed17.0":"2022-03-11T13:57:49.269Z","4.19.0":"2022-03-11T14:10:10.309Z","4.19.1--canary.273.cbb41d41920b77f2570a8e4091de900bc17fa2d5.0":"2022-03-18T13:10:55.243Z","4.19.1":"2022-03-21T07:43:15.762Z","4.20.0--canary.275.93acba31430653fbcdb3646c34fb8fab29cdf935.0":"2022-03-28T08:22:39.788Z","4.20.0--canary.276.2d9bb95809576d4851cea1df48e57c4d48c26be7.0":"2022-03-31T09:48:39.627Z","4.20.0--canary.276.19be28fbea70121c84abc90c79eedab816960744.0":"2022-03-31T09:55:06.385Z","4.20.0--canary.276.969ada9a6d169bcb0a1fc4d36ef206d535d569ea.0":"2022-03-31T10:15:56.597Z","4.20.0--canary.276.96dd5035304b0d12b44ad0a4168e169648018760.0":"2022-03-31T10:45:13.183Z","4.20.0--canary.276.0b86ed74d732ebec78a690d6b83de6791a456581.0":"2022-04-04T11:26:19.005Z","4.20.0":"2022-04-04T14:34:35.888Z","4.20.1--canary.277.b1f79a79bc166424331fca6842e09020ff254f64.0":"2022-04-05T11:58:00.960Z","4.20.1--canary.278.a1fb3d0e1c952c648956184c41226fadee9bf732.0":"2022-04-07T08:03:52.512Z","4.20.1--canary.278.6b5b00cbe544f75d5f7c5c5637d15f05c5427f97.0":"2022-04-07T08:15:55.619Z","4.20.1--canary.1.42882348281daff5003541ffdb7ebf0000144ae8.0":"2022-04-13T15:55:35.067Z","4.20.1--canary.3.e1f3d78acef2870ad6f651f121a991d547f838b0.0":"2022-04-14T13:15:14.576Z","4.20.1--canary.4.ec91ce618c1f4d54f66069a3221e17f9cc6113ab.0":"2022-04-14T14:31:24.513Z","4.20.1--canary.4.be8f250fe5a2e56e398b1bd85b407b0b61311b08.0":"2022-04-14T14:44:34.312Z","4.20.1--canary.5.919edae5629fb16ac539a1c9a44247792c813209.0":"2022-04-14T15:37:10.317Z","5.0.0":"2022-04-14T15:43:26.717Z","5.0.1--canary.6.23fdaf42f2e4bcb9798e566a9b2dc9eb5f0b3a1d.0":"2022-04-18T10:03:57.527Z","5.1.0--canary.7.e3ce281b5547f418b4fcc6bf10049972ea8bf9b8.0":"2022-04-18T11:00:46.503Z","5.0.1--canary.6.b65af659d59a65342fb88647b2baf8545954e7f2.0":"2022-04-18T12:23:48.324Z","5.0.1--canary.8.d353094d51b9f17f2357a4dabb88993db09c2134.0":"2022-04-18T13:40:19.806Z","5.1.0--canary.9.6a2ceb3fe870c1eb29eb939e10006179333521ca.0":"2022-04-19T08:05:21.758Z","5.1.0--canary.9.0624e179e6530344c354847689f2dbd59e11e7c9.0":"2022-04-19T08:07:33.398Z","5.1.0--canary.9.f1608f5ee0108c3c45cc36fcc118c79535bb97e3.0":"2022-04-19T08:09:31.407Z","5.0.1--canary.8.a940894e6693c7792ae329052ac73fbe4f179f55.0":"2022-04-19T08:16:32.502Z","5.1.0--canary.9.e75e9b634ada4b78c12180a3dd881319850ea45f.0":"2022-04-19T08:33:44.013Z","5.0.1--canary.8.38abe1837f16c59d945fe2c3bc8dac75a8051087.0":"2022-04-19T08:35:52.132Z","5.0.1--canary.8.1ccb713bb797fa64883d89d5fcb8ca7a5f83c36e.0":"2022-04-19T08:50:13.126Z","5.0.1":"2022-04-19T09:22:55.584Z","5.1.0--canary.6.6cb0d2c05b1b21c8391789a76cd71dd9d69c44e3.0":"2022-04-19T10:18:56.474Z","5.0.2":"2022-04-21T12:04:23.923Z"},"maintainers":[{"email":"ipuncho@gmail.com","name":"turanchoks"},{"email":"sberdevices.frontend@gmail.com","name":"sberdevices-frontend"}],"description":"Модуль взаимодействия с виртуальным ассистентом","homepage":"https://github.com/sberdevices/assistant-client#readme","keywords":["sber","assistant","smartapp"],"repository":{"type":"git","url":"git+ssh://git@github.com/sberdevices/assistant-client.git"},"author":{"name":"SberDevices Frontend Team","email":"sberdevices.frontend@gmail.com"},"bugs":{"url":"https://github.com/sberdevices/assistant-client/issues"},"license":"Sber Public License at-nc-sa v.2","readme":"[![npm ui](https://img.shields.io/npm/v/@sberdevices/assistant-client)](https://www.npmjs.com/package/@sberdevices/assistant-client)\n\n<img src=\"https://user-images.githubusercontent.com/982072/97004635-0888a900-1546-11eb-8f25-283a0693608e.png\" height=\"150px\" width=\"150px\">\n\n\nAssistant Client — это инструмент для локального тестирования и отладки [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp) c виртуальным ассистентом. Он реализован в виде JavaScript протокола, который эмулирует среду Android и вызывает нативные методы. Такой подход не требует от разработчика наличия физических устройств и позволяет запустить виртуального ассистента через браузер.\n\nПример: [Todo смартап](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app), который демонстрирует взаимодействие с Assistant Client.\n\n## Оглавление\n   * [Конфигурация](#Конфигурация)\n     * [Аутентификация](#Аутентификация)\n     * [Требования](#требования-к-устройствам)\n     * [Установка](#Установка)\n     * [Использование](#пример-использования)\n   * [API](#API)\n     * [createAssistant](#createAssistant)\n     * [createSmartappDebugger](#createSmartappDebugger)\n     * [AssistantClient](#assistantclient)\n   * [Форматы объектов](#форматы-объектов)\n     * [AssistantAppState](#AssistantAppState)\n     * [AssistantServerAction](#AssistantServerAction)\n     * [AssistantCharacterCommand](#AssistantCharacterCommand)\n     * [AssistantNavigationCommand](#AssistantNavigationCommand)\n     * [AssistantInsetsCommand](#AssistantInsetsCommand)\n     * [AssistantThemeCommand](#AssistantThemeCommand)\n     * [AssistantSmartAppError](#AssistantSmartAppError)\n     * [AssistantSmartAppCommand](#AssistantSmartAppCommand)\n   * [Пульт](#пульт)\n     * [Нажатие кнопок на пульте](#нажатие-кнопок-на-пульте)\n     * [Навигация по смартапу](#навигация-по-смартапу)\n   * [Утилиты для тестирования](#утилиты-для-тестирования)\n     * [Имитация команд ассистента](#имитация-команд-ассистента)\n     * [Запись лога сообщений](#запись-лога-сообщений)\n     * [Воспроизведение лога сообщений](#воспроизведение-лога-сообщений)\n* [FAQ](#faq)\n\n____\n\n## Конфигурация\n\n### Аутентификация\n\nДля работы с Assistant Client необходимо:\n\n1. Завести аккаунт в [SmartApp Studio](https://smartapp-studio.sberdevices.ru/).\n2. Создать смартап типа [Сanvas App](https://smartapp-code.sberdevices.ru/documentation/#/docs/ru/methodology/research/canvasapp).\n3. Получить токен. Для этого необходимо перейти в *Настройки профиля* > пункт *Auth Token* > опция *Скопировать ключ*.\n4. Передать полученный токен в методе `createSmartappDebugger` в параметре `token`.\n\n\n### Требования к устройствам\n\nСмартапы должны корректно отображаться на разных устройствах (SberBox, SberPortal и др). Для этого необходимо проверять смартап на следующих разрешениях: 559x568, 768x400, 959x400, 1920x1080. Настроить эти разрешения можно на [вкладке Devices Chrome](https://developers.google.com/web/tools/chrome-devtools/device-mode#custom).\n\n\n### Установка\n\nДля установки Assistant Client выполните следующую команду:\n\n```sh\n$ npm i @sberdevices/assistant-client\n```\n\n### Пример использования\n\n```typescript\n// Функция createSmartappDebugger используется в development среде. В production среде необходимо использовать createAssistant.\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initialize = (getState, getRecoveryState) => {\n    if (process.env.NODE_ENV === 'development') {\n        return createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState,\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState,\n            // Необязательные параметры панели, имитирующей панель на реальном устройстве\n            nativePanel: {\n                // Стартовый текст в поле ввода пользовательского запроса\n                defaultText: 'Покажи что-нибудь',\n                // Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве\n                screenshotMode: false,\n                // Атрибут `tabindex` поля ввода пользовательского запроса\n                tabIndex: -1,\n            },\n        });\n    }\n\n\t  // Только для среды production\n    return createAssistant({ getState, getRecoveryState });\n}\n\n...\n\nconst assistant = initialize(() => state, () => recoveryState);\nassistant.on('data', (command) => {\n    // Подписка на команды ассистента, в т.ч. команда инициализации смартапа.\n    // Ниже представлен пример обработки голосовых команд \"ниже\"/\"выше\"\n    if (command.navigation) {\n        switch(command.navigation.command) {\n            case 'UP':\n                window.scrollTo(0, 0);\n                break;\n            case 'DOWN':\n                window.scrollTo(0, 1000);\n                break;\n        }\n    }\n});\n\nconst handleOnClick = () => {\n    // Отправка сообщения ассистенту с фронтенд.\n    // Структура может меняться на усмотрение разработчика, в зависимости от бэкенд\n    assistant.sendData({ action: { type: 'some_action_name', payload: { param: 'some' } } });\n};\n\nconst handleOnRefreshClick = () => {\n    // Отправка сообщения бэкенду с возможностью подписки на ответ.\n    // В обработчик assistant.on('data') сообщение не передается\n    const unsubscribe = assistant.sendAction(\n        { type: 'some_action_name', payload: { param: 'some' } },\n        (data: { type: string; payload: Record<string, unknown> }) => {\n            // Обработка данных, переданных от бэкенд\n            unsubscribe();\n        },\n        (error: { code: number; description: string }) => {\n            // Обработка ошибки, переданной от бэкенд\n        });\n}\n```\n\n### Альтернативное подключение\n\nAssitant Client доступен для подключения через `<script>`.\nВерсию assistant сlient можно поменять в src. Доступ к API осуществляется через глобальную переменную `assistant`.\n\nПример, для разработки и отладки в браузере (в этом случае обязательно подключение react):\n```html\n<script crossorigin src=\"https://unpkg.com/react@17/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@17/umd/react-dom.production.min.js\"></script>\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.development.min.js\"></script>\n<script>\n  const client = assistant.createSmartappDebugger({\n            // Токен из Кабинета разработчика\n            token: 'token',\n            // Пример фразы для запуска смартапа\n            initPhrase: 'Хочу попкорн',\n            // Текущее состояние смартапа\n            getState: () => ({}),\n            // Состояние смартапа, с которым он будет восстановлен при следующем запуске\n            getRecoveryState: () => ({}),\n        });\n</script>\n```\n\nПример, для использования на устройствах:\n```html\n<script src=\"https://unpkg.com/@sberdevices/assistant-client@4.7.0/umd/assistant.production.min.js\"></script>\n<script>\n  const client = assistant.createAssistant({ getState: () => ({}), getRecoveryState: () => ({}), });\n</script>\n```\n\n____\n\n\n## API\n\n### `createAssistant`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) для запуска виртуального ассистента. Используется на устройствах в production среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :------: | :------------------------------------------------------------------------- |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n\n\n\n### `createSmartappDebugger`\n\nСоздает экземпляр [`AssistantClient`](#assistantclient) и добавляет на экран браузера панель с голосовым ассистентом (подобно устройствам). Панель ассистента находится в нижней части отрисованного экрана и позволяет отправлять виртуальному ассистенту следующие типы сообщений:\n* текстовые сообщения через текстовое поле ввода;\n* голосовые сообщения через кнопку «Салют».\n\n`createSmartappDebugger` используется для локальной отладки и разработки в development среде.\n\n| Параметр         | Обязательный | Описание                                                                   |\n| :--------------- | :----------: | :------------------------------------------------------------------------- |\n| token            | Да           |  Токен из SmartApp Studio                                             |\n| initPhrase       | Да           |  Фраза, которая запускает смартап                                |\n| getState         | Да           |  Функция, которая возвращает актуальное состояние смартапа                 |\n| getRecoveryState | Нет          |  Функция, которая сохраняет состояние смартапа на момент последнего закрытия |\n| settings         | Нет          |  Объект [настроек ассистента](#AssistantSettings)                          |\n| nativePanel      | Нет          |  Объект настроек панели ассистента                          |\n| surface          | Нет          |  Строка, название поверхности. Возможные значения: `SBERBOX` (SberBox), `STARGATE` (SberPortal), `SATELLITE` (SberBox Top) `SBOL` (приложение СберБанк Онлайн), `COMPANION` (приложение Салют), `TV` (Салют ТВ), `TV_HUAWEI` (Huawei Vision), `TIME` (SberBox Time)  |\n\n#### Свойства [Settings](#AssistantSettings)\n\n| Свойство | Значения     | По умолчанию | Описание                      |\n| :------- | :----------: | :----------: | :---------------------------- |\n| dubbing  | true / false | true         | Озвучивание ответа ассистента |\n\n#### Свойства `nativePanel`\n\nВсе свойства являются необязательными.\n\n| Свойство        | Тип значения  | По умолчанию | Описание                                                                                |\n| :-------------- | :-----------: | :----------: | :-------------------------------------------------------------------------------------- |\n| defaultText     | string        | Покажи что-нибудь | Стартовый текст в поле ввода пользовательского запроса                          |\n| screenshotMode  | boolean       | false        | Позволяет включить вид панели, максимально приближенный к панели на реальном устройстве |\n| tabIndex        | number        | -1           | Атрибут `tabindex` поля ввода пользовательского запроса                                 |\n\n### AssistantClient\n\n### cancelTts(): void\n\nОпциональный метод. Вызывается со стороны смартапа для остановки озвучивания ответа (pronounceText). Имеет значение undefined, если устройство не поддерживает данную функцию.\n\n#### close(): void\n\nВызывается со стороны смартапа для завершения работы.\n\n#### getInitialData(): [AssistantCommands](#AssistantCommands)[]\n\nВозвращает данные, полученные при инициализации смартапа. Передает в Assistant Client текущего ассистента, а не ассистента, выставленного по умолчанию.\nЕсли при запуске смартапа не вызвать команду `getInitialData()`, то команды из `appInitialData` будут отправляться в `on('data')`.\n\n#### getRecoveryState(): unknown\n\nВозвращает состояние, сохраненное при закрытии смартапа. Устройство запоминает последнее состояние, которое возвращает функция `getRecoveryState` при инициализации Assistant Client.\n\n#### on('start', cb: () => void): void\n\nОсуществляет подписку на событие готовности ассистента к работе.\n\n#### on('data', cb: (data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantInsetsCommand](#AssistantInsetsCommand) | [AssistantThemeCommand](#AssistantThemeCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => {}): void\n\nОсуществляет подписку на событие получения данных с бэкенда. Получает команды из `appInitialData`, если при запуске смартапа не была вызвана команда `getInitialData()`.\n\n#### sendAction({ type: string; payload: Record<string, unknown> }, params?: { name?: string; requestId?: string }) => void\n\nПередает ошибки и обработчики ответа от бэкенда. <br>\n`sendAction` — отправляет server-action и типизирует сообщения data и error.<br>\n`clear()` — делает отписку от сообщений бэкенда. Это означает, что сообщения не будут переданы в обработчик `assistant.on('data')`\n\n\nПример:\n```ts\nimport { AssistantSmartAppCommand } from '@sberdevices/assistant-client';\n\ninterface SomeBackendMessage extends AssistantSmartAppCommand['smart_app_data'] {\n  type: 'target_action',\n  payload: {\n    data: ['some_data'],\n  },\n}\n\nconst unsubscribe = assistant.sendAction<SomeBackendMessage>({ type: 'some_action_name', payload: { someParam: 'some_value' } },\n  ({ payload }) => {\n    // обработка payload.data\n    unsubscribe();\n  }, (error) => {});\n```\n\n#### sendData({ action: [AssistantServerAction](#AssistantServerAction), requestId?: string }, onData?: data: [AssistantCharacterCommand](#AssistantCharacterCommand) | [AssistantNavigationCommand](#AssistantNavigationCommand) | [AssistantSmartAppError](#AssistantSmartAppError) | [AssistantSmartAppCommand](#AssistantSmartAppCommand)) => void): () => void\n\nОтправляет события с фронтенда на бэкенд через ассистента.\nПервый параметр (обязательный) принимает данные для отправки.\nВторой параметр (опциональный) принимает обработчик ответа (на переданные первым параметром данные). В этом случае в `on('data')` ответ не приходит.\nВозвращает функцию, вызов которой отменяет обработчик ответа.\n\nПример с обработкой ответа:\n```ts\n...\n\nconst unsubscribe = assistant.sendData({ action: { type: 'some_action_name' } }, (data: command) => {\n  if (data.type === 'smart_app_data' && data.smart_app_data.type === 'target_action') {\n    unsubsribe();\n    ... // обработка команды\n  }\n});\n\n```\n\n#### setGetState(nextGetState: () => [AssistantAppState](#AssistantAppState)): void\n\nПодменяет callback, который возвращает актуальное состояние смартапа.\n\n#### setGetRecoveryState(nextGetRecoveryState: () => unknown)\n\nПодменяет callback, который возвращает объект, доступный только при следующем запуске смартапа. Данные приходят при вызове `getRecoveryState`.\n\n\n____\n\n\n## Форматы объектов\n\n### `AssistantAppState`\n\nОбъект `AssistantAppState` — текущее состояние смартапа, которое не хранится в платформе или сценарии. Каждый раз, когда пользователь начинает говорить, Assistant Client вызывает `getState`, чтобы получить и передать в бэкенд состояние экрана пользователя.\nТо, что происходит на экране у пользователя и как он взаимодействует со смартапом в конкретный момент времени — ответственность смартапа. Assistant Client в данном случае — это буфер, который только передает состояние платформе или сценарию.\n\n\n```typescript\ninterface AssistantAppState {\n  // Любые данные, которые могут потребоваться в бэкенде для принятия решений\n  [key: string]: unknown;\n  item_selector?: {\n    ignored_words?: string[];\n    // Список соответствий между голосовыми командами и действиями в приложении\n    items: AssistantViewItem[];\n  };\n}\n\ninterface AssistantViewItem {\n  // Уникальный (в рамках items) порядковый номер элемента, который назначается смартапом\n  number?: number;\n  // Уникальный id элемента\n  id?: string;\n  // Ключевая фраза, которая должна приводить к данному действию\n  title?: string;\n  // Фразы-синонимы, которые должны приводить к данному действию\n  aliases?: string[];\n  // Проксирование action обратно на бэкенд\n  server_action?: AssistantServerAction;\n  // Выполнение действия от имени пользователя\n  action?: Action | { type: string };\n  // Дополнительные данные для бэкенд\n  [key: string]: unknown;\n}\n```\n\nНапример, когда пользователь говорит «Покажи 1», бэкенд должен понимать, что скрывается за единицей (то есть, какой элемент у пользователя пронумерован этой цифрой). Ниже пример состояния, который позволяет понять бэкенду, что, называя «1», пользователь хочет чипсы.\n\n```js\n{\n  item_selector: {\n    ignored_words: [\"покажи\"],\n    items: [\n      { title: 'Сладкий попкорн' },\n      { title: 'Соленый попкорн' },\n      { title: 'Чипсы', number: 1 },\n      { title: 'Начос', number: 2 },\n      { title: 'Кола', number: 3 }\n    ]\n  }\n}\n```\n\n### `AssistantServerAction`\n\nОбъект `AssistantServerAction` — это любое сообщение, которое отправляется с фронтенда на бэкенд. Оно может быть привязано к ui-элементу и приходить с бэкенд, или формироваться самостоятельно фронтовой частью при обработке событий внутри WebView смартапа.\n\n```typescript\ninterface AssistantServerAction {\n  // Тип Server Action\n  type: string;\n  // Любые параметры\n  payload?: Record<string, unknown>;\n}\n```\n\n### `AssistantCharacterCommand`\n\nОбъект `AssistantCharacterCommand` — информирует смартап о текущем персонаже (Сбер, Афина или Джой). Персонаж может быть изменен в любой момент по инициативе пользователя. Поэтому разработчик может дополнительно добавить обработку таких изменений.\n\n```typescript\ninterface AssistantCharacterCommand {\n  type: \"character\";\n  character: {\n    id: \"sber\" | \"eva\" | \"joy\";\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantNavigationCommand`\n\nОбъект `AssistantNavigationCommand` — команда навигации пользователя по смартапу (вперед, назад, дальше и т. д.). В платформе виртуального ассистента есть стандартные фразы, которые приходят и обрабатываются одинаково для всех смартапов.\n\n```typescript\ninterface AssistantNavigationCommand {\n  // Тип команды\n  type: \"navigation\";\n  // Навигационная команда (направление навигации)\n  navigation: { command: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\" | \"FORWARD\" };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n### `AssistantInsetsCommand`\n\nОбъект `AssistantInsetsCommand` — команда, которая сообщает смартапу о том, что поверх него будет отображен нативный UI и его размеры. В `insets` передаются отступы от краев экрана. Их нужно соблюдать, чтобы не было наложения нативных UI элементов и контента смартапа.\n\n```typescript\ninterface AssistantInsetsCommand {\n  type: 'insets';\n  insets: {\n    left: number;    //px\n    top: number;     //px\n    right: number;   //px\n    bottom: number;  //px\n  };\n}\n```\n\n### `AssistantThemeCommand`\n\nОбъект `AssistantInsetsCommand` - команда, которая сообщает смартапу текущую тему платформы — тёмная или светлая. По умолчанию нужно использовать тёмную тему.\n\n```typescript\ninterface AssistantThemeCommand {\n   type: 'theme';\n   theme: {\n      name: 'dark' | 'light'\n   }\n}\n```\n\n### `AssistantSmartAppError`\n\nОбъект `AssistantSmartAppError` — это уведомление об ошибке.\n\n```typescript\ninterface AssistantSmartAppError {\n  type: 'smart_app_error';\n  smart_app_error: {\n    code: number;\n    description: string;\n  };\n}\n```\n\n### `AssistantSmartAppCommand`\n\nОбъект `AssistantSmartAppCommand` — это команда передачи смартапу любых данных с бэкенда.\n\n```typescript\ninterface AssistantSmartAppCommand {\n  // Тип команды\n  type: \"smart_app_data\";\n  smart_app_data: {\n    type: string;\n    // Любые данные, которые нужны смартапу\n    payload: Record<string, unknown>;\n  };\n  sdk_meta: {\n    requestId: string;\n  };\n}\n```\n\n____\n\n\n## Пульт\n\n### Нажатие кнопок на пульте\n\nДля получения и обработки нажатия кнопок на пульте от SberBox необходимо подписаться на события нажатия клавиш клавиатуры. Пример ниже:\n\n```javascript\nwindow.addEventListener('keydown', (event) => {\n  switch(event.code) {\n    case 'ArrowDown':\n      // вниз\n      break;\n     case 'ArrowUp':\n      // вверх\n      break;\n     case 'ArrowLeft':\n      // влево\n      break;\n     case 'ArrowRight':\n      // вправо\n      break;\n     case 'Enter':\n      // ок\n     break;\n  }\n});\n```\n\n### Навигация по страницам смартапа\n\nДля корректной обработки кнопки `back` и навигации по страницам смартапа необходимо построить историю переходов, используя `History API`. Например, подписываемся на `window.onpopstate` и реализуем изменение страницы в обработчике этого события. Когда хотим выполнить изменение страницы, вызываем `window.history.pushState`:\n\n```typescript\n\nconst [page, setPage] = useState<string>('previous');\n\nconst handleNext = () => {\n  window.history.pushState({ page: 'next' }, ''); // инициируем переход на следующую страницу\n}\n\nuseEffect(() => {\n  window.history.replaceState({ page: 'previous' }, ''); // устанавливаем текущую страницу\n  window.onpopstate = ({ state }) => {\n    setPage(state.page); // выполняем переход на заданную страницу: next - по вызову handleNext или previous - по нажатию кнопки back\n  }\n}, []);\n\n```\n____\n\n\n## Утилиты для тестирования\n\n### Имитация команд ассистента\n\nДля имитации команд от ассистента используйте утилиту `createAssistantHostMock`. Ниже приведен пример использования. Полный пример доступен [по ссылке](https://github.com/sberdevices/assistant-client/tree/main/examples/todo-canvas-app).\n\n```typescript\nimport { createAssistantHostMock } from '@sberdevices/assistant-client';\n\nconst ITEMS = [\n  {\n    id: 1,\n    title: 'Купить молоко',\n    number: 1,\n  },\n  {\n    id: 2,\n    title: 'Купить хлеб',\n    number: 2,\n  },\n];\n\ndescribe('Мой список дел', () => {\n  it('По клику на чекбокс - ожидаем экшен \"done\" c заголовком выбранного элемента', (done) => {\n    cy.visit('/')\n      .window()\n      .then((window) => {\n        const mock = createAssistantHostMock({ context: window });\n        const selected = ITEMS[1];\n        mock.onReady(() => {\n          // эмулируем инициализационную команду от бэкенда со списком задач\n          mock.receiveCommand({\n            type: 'smart_app_data',\n            action: {\n              type: 'init',\n              notes: [...ITEMS],\n            },\n          })\n          .then(() =>\n            // ожидаем вызов assistantClient.sendData\n            mock.waitAction(() =>\n                // эмулируем отметку выполнения пользователем, который должен вызвать sendData({ action: { type: 'done } })\n                window.document.getElementById(`checkbox-note-${selected.id}`).click(),\n            ),\n          )\n          .then(({ action, state }) => {\n            expect(action.type).to.equal('done'); // ожидаем экшен data_note\n            expect(action.payload?.title).to.equal(selected.title); // ожидаем в параметрах title экшена\n            expect(state?.item_selector.items).to.deep.equal(ITEMS); // ожидаем отправку списка в стейте\n            done();\n          });\n        });\n      });\n  });\n});\n```\n\n`createAssistantHostMock` можно вызывать только при использовании [`createAssistant`](#createAssistant). Например, при использовании `cypress` функция инициализации ассистента может выглядеть следующим образом:\n\n```typescript\nimport { createAssistant, createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst initializeAssistant = (getState: AssistantAppState) => {\n    if (process.env.NODE_ENV === 'development' && window.Cypress == null) {\n        return createSmartappDebugger({\n            token: process.env.REACT_APP_TOKEN ?? '',\n            initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n            getState,\n        });\n    }\n\n    if (window.cypress) {\n      window.appInitialData = [];\n    }\n\n    return createAssistant({ getState });\n};\n```\n\n#### addActionHandler(actionType: string, handler: (action: AssistantServerAction) => void): void\n\nПодписка на экшены фронтенда с определенным type, который передается первым параметром.\n\n#### removeActionHandler(actionType: string): void\n\nОтмена подписки от экшенов фронтенда.\n\n#### receiveCommand(command: AssistantClientCommand): Promise<void>\n\nЭмуляция команды, полученной от бэкенда. Команда приходит подписчикам `AssistantClient.onData`.\n\n#### waitAction(onAction?: () => void): Promise<{ state: AssistantAppState; action: AssistantServerAction; name?: string; requestId?: string; }>\n\nПолучение `promise`, который будет разрезолвлен при следующем вызове `AssistantClient.sendData`\n\n#### onReady(cb: () => void): void\n\nПодписка на события готовности утилиты. Параметр `cb` будет вызван по готовности к работе.\n\n\n### Запись лога сообщений\n\nВ режиме разработки есть возможность записать и скачать лог сообщений.\nУправление записью осуществляется кнопками `start` и `stop`. Кнопка `save` сохранит файл с логом в загрузки браузера. Пример активации панели управления записью лога:\n\n```typescript\nimport { createSmartappDebugger } from '@sberdevices/assistant-client';\n\nconst assistant = createSmartappDebugger({\n    token: process.env.REACT_APP_TOKEN ?? '',\n    initPhrase: `Запусти ${process.env.REACT_APP_SMARTAPP}`,\n    getState,\n    enableRecord: true, // активируем функцию записи лога\n    recordParams: {\n      defaultActive: true, // включать запись при старте приложения (по-умолчанию = true)\n    }\n  });\n```\n\n### Воспроизведение лога сообщений\n\nПример пошагового воспроизведения лога сообщений. Входящие сообщения от ассистента будут последовательно переданы подписчикам `AssistantClient.on('data')`.\n\n```typescript\nimport { createRecordPlayer } from '@sberdevices/assistant-client';\nimport assistantLog from './assistant-log.json';\n\nconst player = createRecordPlayer(assistantLog);\nlet end = false;\n\nwhile(!end) {\n  end = !player.continue();\n}\n```\n\n#### continue(): boolean\n\nПередает следующее сообщение от ассистента в AssistantClient (может содержать несколько команд). Возвращает флаг наличия в логе следующих сообщений от ассистента.\n\n#### play(): void\n\nПоследовательно передает все сообщения лога от ассистента в AssistantClient.\n\n#### getNextAction: { action: AssistantServerAction; name?: string; requestId?: string; }\n\nВозвращает следующее сообщение от AssistantClient (вызов `sendData`) в ассистент. Можно использовать для сравнения эталонного сообщения (из лога) и текущего в тесте.\n\n#### setRecord(record: AssistantRecord): void\n\nЗагружает указанную запись в плеер.\n\n\n____\n\n\n## FAQ\n\n### Не работает озвучка и/или микрофон в браузере\n\nНужно перейти в [настройки сайта](https://support.google.com/chrome/answer/114662) и разрешить доступ к звуку и микрофону.\n","readmeFilename":"README.md"}